winform中richtextbox组件设置字间距

winform中richtextbox组件设置字间距

winform中richtextbox组件设置字间距

在最近的一个项目中需要对richtextbox的内容设置字间距,但是网上搜了一圈发现richtextbox组件并不支持这个功能,而且网上也没法先很方便的实现。

但是从各种途径发现RTF规范里面是有一个关键词(\expndtw)设置字间距(以缇为单位,DIP为96情况下 15缇=1像素)的,在实际试过后在richtextbox组件里确实能够显示,于是接下来只要操作richtextbox的rtf字符串进行修改就可以了。

以下是我实现的代码,只在.net framwork4.8和4.72测试过。

private void tcbLetterSpace_SelectedIndexChanged(object sender, EventArgs e)
{
	int space;
	if (int.TryParse((string)tcbLetterSpace.Text, out space))
	{
		int SelectionStart = rtbEditor.SelectionStart;
		int SelectionLength = rtbEditor.SelectionLength;
		// TODO: 如果插入的位置不是字符串末尾
		// 则直接插入控制语句即可
		// 否则要将此次的设置保存下来
		// 在输入下一个字符是才开始生效
		string originRTF = (string)rtbEditor.Rtf.Clone();
		string rtfStr = (string)rtbEditor.Rtf.Clone();

		// 找到需要插入字符间距的起始位点
		int rtfIndex = SelectionRTFIndex(rtfStr, rtbEditor.SelectionStart);

		// 插入expndtw,以twips为单位,(15 twips = 1 pixel)
		Console.WriteLine("rtfIndex is {0}", rtfIndex);
		Console.WriteLine("Before Insert: {0}", rtfStr);
		rtfStr = rtfStr.Insert(rtfIndex, $"\\expndtw{space * 15} ");
		Console.WriteLine("After Insert: {0}", rtfStr);

		if (SelectionLength > 0)
		{
			// 插入完成后调整所选内容之后的字间距
			// 将所选内容的所有 \expndtw
			// 清空并获得末尾字符后一个字符前的字间距
			space = CleanSelectionLetterSpace(ref rtfStr, originRTF, SelectionStart, SelectionLength);
			if (space >= 0)
			{
				rtfIndex = SelectionRTFIndex(rtfStr, SelectionStart + SelectionLength);
				rtfStr = rtfStr.Insert(rtfIndex, $"\\expndtw{space} ");
			}
			Console.WriteLine("After Clean: {0}", rtfStr);
		}

		rtbEditor.Rtf = rtfStr;
		rtbEditor.SelectionStart = SelectionStart + SelectionLength;
		rtbEditor.Focus();
	}
}

private int SelectionRTFIndex(string rtfStr, int textIndex)
{
	double count = 0;
	bool isTextBegin = false;
	bool isSentenceBegin = false;
	MemoryStream ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(rtfStr));
	int ch;
	string token;
	while (true)
	{
		ch = ms.ReadByte();

		if (ch.Equals('\\'))
		{
			isSentenceBegin = false;
			token = RTFHelper.NextToken(ref ms);
			if (token.Equals("pard"))
			{
				isTextBegin = true;
			}
			else if (token.Equals("par"))
			{
				if (isTextBegin)
				{
					isSentenceBegin = true;
					// 增加数量前进行判断的原因是防止含有其它无关字符
					if (count == textIndex && isSentenceBegin && isTextBegin)
					{
						ms.Position -= 4;
						goto DONE;
					}

					count += 1;
				}
			}
			else if (token.Contains("'"))
			{
				if (isTextBegin)
				{
					isSentenceBegin = true;
					if (count == textIndex && isSentenceBegin && isTextBegin)
					{
						ms.Position -= @"\'ff".Length;
						goto DONE;
					}
					
					// GB2312一个字符(非asc)由两个字节组成
					count += 0.5;
					
					// 考虑中文后面跟着其它字符的情况
					if (token.Length > 3)
					{
						count += token.Length - 3; 
					}
				}
			}
		}
		else if (ch != -1)
		{
			if (isTextBegin)
			{
				if (ch.Equals(' '))
				{
					if (isSentenceBegin)
					{
						if (count == textIndex && isSentenceBegin && isTextBegin)
						{
							ms.Position -= 1;
							goto DONE;
						}
						count++;
					}
					else
					{
						isSentenceBegin = true;
					}
				}
				else if(!RTFHelper.IsDelimiter(ch))
				{
					if (count == textIndex && isSentenceBegin && isTextBegin)
					{
						ms.Position -= 1;
						goto DONE;
					}
					count++;
				}
			}
		}
		else
		{
			goto DONE;
		}
	}
DONE:
	return (int)ms.Position;
}

private int CleanSelectionLetterSpace(ref string rtfStr, string originRTF, int SelectionStart, int SelctionLength)
{
	int space = 0;
	int subBegin = 0, subEnd = 0;
	string subStr = string.Empty;

	if (SelctionLength > 1)
	{
		subEnd = SelectionRTFIndex(originRTF, SelectionStart + SelctionLength);
		MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(originRTF.Substring(0, subEnd)));

		// 获得最后的 expndtw 值
		int ch;
		string token;
		while (true)
		{
			ch = ms.ReadByte();

			if (ch.Equals('\\'))
			{
				token = RTFHelper.NextToken(ref ms);
				if (RTFHelper.GetNonNumericString(token).Equals("expndtw"))
				{
					if (!int.TryParse(RTFHelper.GetNumericString(token), out space))
					{
						space = -1;
						goto DONE;
					}
				}
			}

			if (ch == -1)
			{
				goto DONE;
			}
		}
	}
DONE:
	subBegin = SelectionRTFIndex(rtfStr, SelectionStart);
	subEnd = SelectionRTFIndex(rtfStr, SelectionStart + SelctionLength);
	try
	{
		subStr = rtfStr.Substring(subBegin, subEnd - subBegin);
	}
	catch (ArgumentOutOfRangeException ex)
	{
		Console.WriteLine("出于不知名的原因,在清理字间距获得字串时出错......{0}", ex);
	}
	if (space != -1)
	{
		// 根据正则表达式删除控制语句
		subStr = Regex.Replace(subStr, "\\\\expndtw\\d*\\s?", "");
		rtfStr = rtfStr.Remove(subBegin, subEnd - subBegin);
		rtfStr = rtfStr.Insert(subBegin, subStr);
	}
	return space;
}

使用方法

将以上代码复制粘贴后,将点击按钮事件设置为tcbLetterSpace_SelectedIndexChanged并设置richtextbox组件名为rtbEditor即可,或直接修改代码。注意,如果在行末设置行间距的话没有效果,因为会被自动清理掉,如有需要应该要将操作保存下来,并在下一次末尾添加字符时重新添加。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值