// 将一个<p>元素内容切割为Line数组 int CutLines(Graphics g, RectParam rect_def, XmlNode nodeP, out List<Line> lines, out string strError) { strError = ""; lines = new List<Line>(); float fLineHeight = 0; string strFontString = ""; string strText = nodeP.InnerText; if (String.IsNullOrEmpty(strText) == true) { // TODO 只有一行的高度 using (Font font = new Font("Times New Roman", 16.0F)) { // TODO: 需要测试一下空字符串是否可以用于MesasureString() SizeF size = g.MeasureString(strText, font); fLineHeight = size.Height; strFontString = GetFontString(font); } Line current_line = new Line(); current_line.Height = fLineHeight; current_line.FontDef = strFontString; lines.Add(current_line); return 0; } CharacterRange[] ranges = new CharacterRange[strText.Length]; for (int i = 0; i < strText.Length; i++) { ranges[i] = new CharacterRange(i, 1); } StringFormat format = new StringFormat(); format.FormatFlags = StringFormatFlags.NoClip; format.SetMeasurableCharacterRanges(ranges); Region[] regions = new Region[strText.Length]; using (Font font = new Font("Times New Roman", 16.0F)) { SizeF size = g.MeasureString(strText, font); fLineHeight = size.Height; RectangleF layout = new RectangleF(0, 0, size.Width, size.Height); regions = g.MeasureCharacterRanges( strText, font, layout, format); strFontString = GetFontString(font); } // 切割为若干行 { Line current_line = new Line(); current_line.Height = fLineHeight; current_line.FontDef = strFontString; lines.Add(current_line); float x = 0; for (int i = 0; i < regions.Length; i++) { Region region = regions[i]; RectangleF rect = region.GetBounds(g); if (x > rect_def.Width) { // 另起一行 current_line = new Line(); current_line.Height = fLineHeight; current_line.FontDef = strFontString; lines.Add(current_line); Box box = new Box(); box.Text = strText.Substring(i, 1); box.Width = rect.Width; box.Height = rect.Height; current_line.Boxes.Add(box); x = 0; } else { Box box = new Box(); box.Text = strText.Substring(i, 1); box.Width = rect.Width; box.Height = rect.Height; current_line.Boxes.Add(box); } x += rect.Width; } } return 0; }
public RectParam(RectParam rect_ref) { this.X = rect_ref.X; this.Y = rect_ref.Y; this.Width = rect_ref.Width; this.WidthString = rect_ref.WidthString; this.Padding = new Padding(rect_ref.Padding.Left, rect_ref.Padding.Top, rect_ref.Padding.Right, rect_ref.Padding.Bottom); this.Height = rect_ref.Height; }
// 将一个字符串切割为Line数组 // parameters: // strTextParam ==null 表示触发<p>前面的初始化换行 // == "\r\n" 执行<br/>动作 int BuildLines(Graphics g, RectParam rect_def, string strTextParam, Font font, Color color, ParagraphFormat p_format, ref List<PrintLine> lines, out string strError) { strError = ""; int nRet = 0; float fLineHeight = 0; string strFontString = GetFontString(font); string strFontColor = GetColorString(color); // 表示建立一个空的<p>元素显示位置 if (strTextParam == null) { SizeF size = g.MeasureString("1", font); fLineHeight = size.Height; float emheight = font.FontFamily.GetEmHeight(FontStyle.Regular); float ascent = font.FontFamily.GetCellAscent(FontStyle.Regular); PrintLine current_line = null; current_line = new PrintLine(); current_line.HorzAlign = GetHorzAlign(p_format.Align); current_line.Height = fLineHeight; current_line.Width = rect_def.Width - rect_def.Padding.Horizontal; current_line.FontDef = strFontString; current_line.ColorDef = strFontColor; current_line.BaseRatio = ascent / emheight; lines.Add(current_line); current_line.Indent = p_format.Indent; return 0; } // 回车换行 if (strTextParam == "\r\n") { SizeF size = g.MeasureString("1", font); fLineHeight = size.Height; float emheight = font.FontFamily.GetEmHeight(FontStyle.Regular); float ascent = font.FontFamily.GetCellAscent(FontStyle.Regular); PrintLine current_line = null; // 另起一行 current_line = new PrintLine(); current_line.HorzAlign = GetHorzAlign(p_format.Align); current_line.Height = fLineHeight; current_line.Width = rect_def.Width - rect_def.Padding.Horizontal; current_line.FontDef = strFontString; current_line.ColorDef = strFontColor; current_line.BaseRatio = ascent / emheight; lines.Add(current_line); current_line.Indent = p_format.Indent; return 0; } for (int j=0; ;j++ ) { if (String.IsNullOrEmpty(strTextParam) == true) break; float emheight = font.FontFamily.GetEmHeight(FontStyle.Regular); float ascent = font.FontFamily.GetCellAscent(FontStyle.Regular); int nLen = Math.Min(30, strTextParam.Length); string strText = strTextParam.Substring(0, nLen); strTextParam = strTextParam.Remove(0, nLen); CharacterRange[] ranges = new CharacterRange[strText.Length]; for (int i = 0; i < strText.Length; i++) { ranges[i] = new CharacterRange(i, 1); } StringFormat format = new StringFormat(); format.FormatFlags = StringFormatFlags.NoClip ; format.SetMeasurableCharacterRanges(ranges); Region[] regions = new Region[strText.Length]; { SizeF size = g.MeasureString(strText, font); fLineHeight = size.Height; RectangleF layout = new RectangleF(0, 0, size.Width, size.Height); regions = g.MeasureCharacterRanges( strText, font, layout, format); } // 切割为若干行 { float x = 0; PrintLine current_line = null; if (lines.Count == 0) { current_line = new PrintLine(); current_line.HorzAlign = GetHorzAlign(p_format.Align); current_line.Height = fLineHeight; current_line.Width = rect_def.Width - rect_def.Padding.Horizontal; current_line.FontDef = strFontString; current_line.ColorDef = strFontColor; current_line.BaseRatio = ascent / emheight; lines.Add(current_line); x = p_format.Indent; current_line.Indent = p_format.Indent; } else { current_line = lines[lines.Count - 1]; x = GetLineWidth(current_line) + current_line.Indent; } for (int i = 0; i < regions.Length; i++) { string strCurrentChar = strText.Substring(i, 1); Region region = regions[i]; RectangleF rect = region.GetBounds(g); if (x + rect.Width > rect_def.Width - rect_def.Padding.Horizontal) { if (p_format.LineBreak == "word") { // return: // -1 无需特殊处理 // 其他 需要切断的位置 nRet = DetectLineBreak(strCurrentChar, current_line); } else nRet = -1; if (nRet != -1) { PrintLine prev_line = current_line; // 另起一行 current_line = new PrintLine(); current_line.HorzAlign = GetHorzAlign(p_format.Align); current_line.Width = rect_def.Width - rect_def.Padding.Horizontal; current_line.BaseRatio = prev_line.BaseRatio; lines.Add(current_line); x = 0; int nStart = nRet; // 准备字体、颜色字符串 string strTempFontString = prev_line.FontDef; string strTempColorString = prev_line.ColorDef; for (int k = 0; k <= nStart; k++) { Box box = prev_line.Boxes[k]; if (string.IsNullOrEmpty(box.FontDef) == false) strTempFontString = box.FontDef; if (string.IsNullOrEmpty(box.ColorDef) == false) strTempColorString = box.ColorDef; } current_line.FontDef = strTempFontString; current_line.ColorDef = strTempColorString; // 切断,同时获得行高度 float fTempLineHeight = 0; int nCount = prev_line.Boxes.Count; for (int k = nStart; k < nCount; k++) { Box box = prev_line.Boxes[nStart]; current_line.Boxes.Add(box); prev_line.Boxes.RemoveAt(nStart); if (fTempLineHeight < box.Height) fTempLineHeight = box.Height; } current_line.Height = fTempLineHeight; } else { // 另起一行 current_line = new PrintLine(); current_line.HorzAlign = GetHorzAlign(p_format.Align); current_line.Height = fLineHeight; current_line.Width = rect_def.Width - rect_def.Padding.Horizontal; current_line.FontDef = strFontString; current_line.ColorDef = strFontColor; current_line.BaseRatio = ascent / emheight; lines.Add(current_line); x = 0; } } else { } if (current_line.Boxes.Count == 0 && lines.Count > 1 && strCurrentChar == " ") { // 忽略第二行以后最左端的空格 } else { Box box = new Box(); box.Text = strCurrentChar; box.Width = rect.Width; box.Height = rect.Height; box.Base = fLineHeight * ascent / emheight; if (i == 0 && j == 0) { box.FontDef = strFontString; box.ColorDef = strFontColor; } if (current_line.Height < fLineHeight) { current_line.Height = fLineHeight; } current_line.Boxes.Add(box); x += rect.Width; } } } } return 0; }
// parameters: // state ParagraphFirst 是否为首次处理一个<p> int Process( Graphics g, RectParam rect_def, XmlNode node, Hashtable macro_table, RectGroup rect_group, ref List<PrintLine> lines, out string strError) { strError = ""; int nRet = 0; if (node.NodeType == XmlNodeType.Element && node.Name == "br") { SetLastLine(lines); Color color; Font font = GetNodeFont(node, out color); ParagraphFormat format = GetNodeParagraphFormat(node); try { nRet = BuildLines(g, rect_def, "\r\n", font, color, format, ref lines, out strError); if (nRet == -1) return -1; } finally { font.Dispose(); } return 0; } int nStartLines = lines.Count; int nLinesLimit = -1; ParagraphGroup p_group = null; // <p>的开头,触发一下换行 if (node.NodeType == XmlNodeType.Element && node.Name == "p") { SetLastLine(lines); Color color; Font font = GetNodeFont(node, out color); ParagraphFormat format = GetNodeParagraphFormat(node); nLinesLimit = (int)format.MaxLines; p_group = new ParagraphGroup(); p_group.Format = format; rect_group.Paragraphs.Add(p_group); try { nRet = BuildLines(g, rect_def, null, font, color, format, ref lines, out strError); if (nRet == -1) return -1; } finally { font.Dispose(); } } for (int i = 0; i < node.ChildNodes.Count; i++) { XmlNode child = node.ChildNodes[i]; if (child.NodeType == XmlNodeType.Text) { string strText = child.Value; strText = strText.Replace("\n", "\r"); strText = strText.Replace("\r", ""); if (macro_table != null) { strText = StringUtil.MacroString(macro_table, strText); } Color color; Font font = GetNodeFont(node, out color); ParagraphFormat format = GetNodeParagraphFormat(node); try { nRet = BuildLines(g, rect_def, strText, font, color, format, ref lines, out strError); if (nRet == -1) return -1; } finally { font.Dispose(); } } if (child.NodeType == XmlNodeType.Element) { int x_delta = 0; int y_delta = 0; RectParam current_rect_def = rect_def; if (child.Name == "p") { // 2012/4/23 // 让<p>的padding属性起作用 ParagraphFormat format = GetNodeParagraphFormat(child); current_rect_def = new RectParam(rect_def); current_rect_def.Padding = AddPadding(current_rect_def.Padding, format.Padding); x_delta = format.Padding.Left; y_delta = format.Padding.Top; } int nStartLine = 0; if (lines != null) nStartLine = lines.Count; nRet = Process( g, current_rect_def, child, macro_table, rect_group, ref lines, out strError); if (nRet == -1) return -1; // 对新增的行进行平移 if (x_delta != 0 || y_delta != 0) { for (int j = nStartLine; j < lines.Count; j++) { PrintLine line = lines[j]; // line.X += x_delta; line.Y += y_delta; } } } } // 删除多余的行 if (nLinesLimit != -1) { // TODO 如果发生切断,是不是最后要加... ? while (lines.Count > nStartLines + nLinesLimit) { lines.RemoveAt(lines.Count - 1); } } if (p_group != null) { for (int i = nStartLines; i < lines.Count; i++) { p_group.Lines.Add(lines[i]); } } return 0; }
// 处理一个矩形区域 int DoRect( Graphics g, XmlNode nodeContainer, RectParam rect_def, bool bRemainPage, Hashtable macro_table, RectGroup rect_group, ref List<Page> pages, out string strError) { strError = ""; int nRet = 0; Padding padding = new Padding(0, 0, 0, 0); string strPadding = DomUtil.GetAttr(nodeContainer, "padding"); if (String.IsNullOrEmpty(strPadding) == false) { nRet = GetPadding(strPadding, out padding, out strError); if (nRet == -1) { strError = "元素<" + nodeContainer.Name + ">中padding属性值 '" + strPadding + "' 格式错误: " + strError; return -1; } } rect_def.Padding = padding; List<PrintLine> lines = new List<PrintLine>(); // 将一个容器元素下级的全部内容切割为Line数组 nRet = Process(g, rect_def, nodeContainer, macro_table, rect_group, ref lines, out strError); if (nRet == -1) return -1; SetLastLine(lines); // 组装到Page中 int iPage = 0; Page current_page = null; if (pages.Count > iPage) current_page = pages[iPage]; else { current_page = new Page(); pages.Add(current_page); } float current_height = 0; for (int i = 0; i < lines.Count; i++) { PrintLine line = lines[i]; if (current_height + line.Height > rect_def.Height - rect_def.Padding.Vertical && current_page.Lines.Count > 0 // 至少有一个行 && bRemainPage == false) { // 新增一页 current_height = 0; iPage++; if (pages.Count > iPage) current_page = pages[iPage]; else { current_page = new Page(); pages.Add(current_page); } } // line.X += rect_def.X + rect_def.Padding.Left; line.Y += rect_def.Y + rect_def.Padding.Top + current_height; current_page.Lines.Add(line); current_height += line.Height; } return iPage; }
void ResetLines(List<Page> pages, RectParam rect_def, List<PrintLine> lines) { int iPage = pages.Count - 1; Page current_page = pages[iPage]; float current_height = rect_def.Height; for (int i = lines.Count - 1; i >= 0; i--) { PrintLine line = lines[i]; if (current_height - line.Height < 0) { // 倒退一页 current_height = rect_def.Height; iPage--; if (iPage >= 0) current_page = pages[iPage]; else { break; } } // 把line从pages中原来位置移走 foreach (Page page in pages) { if (page.Lines.IndexOf(line) != -1) { page.Lines.Remove(line); break; } } line.X = rect_def.X + rect_def.Padding.Left; line.Y = rect_def.Y + current_height - rect_def.Padding.Bottom -line.Height ; current_page.Lines.Add(line); current_height -= line.Height; } }
// parameters: // height 分页以前的单页允许高度 // bIsHeaderFooter 是否保持不增加page int DoColumns( Graphics g, XmlNode nodeDocument, float x, float y, float width, float height, bool bIsHeaderFooter, Hashtable macro_table, string strHeaderCondition, ref List<Page> pages, out string strError) { strError = ""; List<RectParam> rects = new List<RectParam>(); // 列出根下面的<Column>元素 XmlNodeList nodes = nodeDocument.SelectNodes("column"); for (int i = 0; i < nodes.Count; i++) { XmlNode node = nodes[i]; // 获得矩形参数 RectParam rect = new RectParam(); rect.WidthString = DomUtil.GetAttr(node, "width"); rects.Add(rect); } // 分配宽度。固定宽度剩下以后的,就是auto的平分 float rest = width; // 剩下的宽度 // 1) 处理数字宽度 int nAutoCount = 0; for (int i = 0; i < rects.Count; i++) { RectParam rect = rects[i]; string strWidth = rect.WidthString; if (StringUtil.IsPureNumber(strWidth) == true) { rect.Width = (float)Convert.ToDouble(strWidth); rest -= rect.Width; } else { if (string.Compare(strWidth, "auto", true) != 0) { strError = "<column>元素的width属性值 '"+strWidth+"' 格式错误"; return -1; } rect.Width = -1; nAutoCount++; } } // 2) 处理auto宽度 if (nAutoCount > 0) { float nAverWidth = rest / nAutoCount; if (nAverWidth < 0) nAverWidth = 0; for (int i = 0; i < rects.Count; i++) { RectParam rect = rects[i]; if (rect.Width == -1) rect.Width = nAverWidth; } } // 3)填充 X Y Height float start_x = x; for (int i = 0; i < rects.Count; i++) { RectParam rect = rects[i]; rect.X = start_x; start_x += rect.Width; rect.Y = y; rect.Height = height; // 一页内最大高度 } List<RectGroup> rect_groups = new List<RectGroup>(); for (int i = 0; i < nodes.Count; i++) { XmlNode node = nodes[i]; if (bIsHeaderFooter == true) { string strStyle = DomUtil.GetAttr(node, "style"); if (StringUtil.IsInList("hidewhenonepage", strStyle) == true && StringUtil.IsInList("onlyonepage", strHeaderCondition) == true) continue; if (StringUtil.IsInList("hidewhenfirstpage", strStyle) == true && StringUtil.IsInList("firstpage", strHeaderCondition) == true) continue; if (StringUtil.IsInList("hidewhentailpage", strStyle) == true && StringUtil.IsInList("tailpage", strHeaderCondition) == true) continue; } // 获得矩形参数 RectParam rect = rects[i]; RectGroup rect_group = new RectGroup(); rect_groups.Add(rect_group); int nRet = DoRect( g, node, rect, bIsHeaderFooter, macro_table, rect_group, ref pages, out strError); if (nRet == -1) return -1; } if (bIsHeaderFooter == false) { // 处理valign=bottom的<p> for (int i = 0; i < nodes.Count; i++) { XmlNode node = nodes[i]; // 获得矩形参数 RectParam rect = rects[i]; RectGroup rect_group = rect_groups[i]; for (int j = rect_group.Paragraphs.Count - 1; j >= 0; j--) { ParagraphGroup p_group = rect_group.Paragraphs[j]; if (p_group.Format.VertAlign == "bottom") { ResetLines(pages, rect, p_group.Lines); } else break; // 只要中间不连续,就中断 } } } return 0; }