public static GLine CreateSimpleGLine(string text, TextDecoration dec) { char[] buff = new char[text.Length * 2]; int offset = 0; int start = 0; CharGroup prevType = CharGroup.LatinHankaku; GWord firstWord = null; GWord lastWord = null; for (int i = 0; i < text.Length; i++) { char originalChar = text[i]; char privateChar = Unicode.ToPrivate(originalChar); CharGroup nextType = Unicode.GetCharGroup(privateChar); int size = CharGroupUtil.GetColumnsPerCharacter(nextType); if (nextType != prevType) { if (offset > start) { GWord newWord = new GWord(dec, start, prevType); if (lastWord == null) { firstWord = lastWord = newWord; } else { lastWord.Next = newWord; lastWord = newWord; } } prevType = nextType; start = offset; } buff[offset++] = originalChar; if (size == 2) { buff[offset++] = WIDECHAR_PAD; } } GWord w = new GWord(dec, start, prevType); if (lastWord == null) { firstWord = w; } else { lastWord.Next = w; } return(new GLine(buff, offset, firstWord)); }
/// <summary> /// Gets number of columns used to display a character. /// </summary> /// <param name="ch">Private character code.</param> /// <returns>1 (half-width character) or 2 (full-width character)</returns> public static int GetCharacterWidth(char ch) { CharGroup charGroup = GetCharGroup(ch); return(CharGroupUtil.GetColumnsPerCharacter(charGroup)); }
internal void Render(IntPtr hdc, RenderProfile prof, Color baseBackColor, int x, int y) { if (_text.Length == 0 || _text[0] == '\0') { return; //何も描かなくてよい。これはよくあるケース } float fx0 = (float)x; float fx1 = fx0; int y1 = y; int y2 = y1 + (int)prof.Pitch.Height; float pitch = prof.Pitch.Width; int defaultBackColorArgb = baseBackColor.ToArgb(); Win32.SetBkMode(hdc, Win32.TRANSPARENT); GWord word = _firstWord; while (word != null) { TextDecoration dec = word.Decoration; IntPtr hFont = prof.CalcHFONT_NoUnderline(dec, word.CharGroup); Win32.SelectObject(hdc, hFont); uint foreColorRef = DrawUtil.ToCOLORREF(prof.CalcTextColor(dec)); Win32.SetTextColor(hdc, foreColorRef); Color bkColor = prof.CalcBackColor(dec); bool isOpaque = (bkColor.ToArgb() != defaultBackColorArgb); if (isOpaque) { uint bkColorRef = DrawUtil.ToCOLORREF(bkColor); Win32.SetBkColor(hdc, bkColorRef); } int nextOffset = GetNextOffset(word); float fx2 = fx0 + pitch * nextOffset; if (prof.CalcBold(dec) || CharGroupUtil.IsCJK(word.CharGroup)) { // It is not always true that width of a character in the CJK font is twice of a character in the ASCII font. // Characters are drawn one by one to adjust pitch. int step = CharGroupUtil.GetColumnsPerCharacter(word.CharGroup); float charPitch = pitch * step; int offset = word.Offset; float fx = fx1; if (isOpaque) { // If background fill is required, we call ExtTextOut() with ETO_OPAQUE to draw the first character. if (offset < nextOffset) { Win32.RECT rect = new Win32.RECT((int)fx1, y1, (int)fx2, y2); char ch = _text[offset]; Debug.Assert(ch != GLine.WIDECHAR_PAD); unsafe { Win32.ExtTextOut(hdc, rect.left, rect.top, Win32.ETO_OPAQUE, &rect, &ch, 1, null); } } offset += step; fx += charPitch; } for (; offset < nextOffset; offset += step) { char ch = _text[offset]; Debug.Assert(ch != GLine.WIDECHAR_PAD); unsafe { Win32.ExtTextOut(hdc, (int)fx, y1, 0, null, &ch, 1, null); } fx += charPitch; } } else { int offset = word.Offset; int displayLength = nextOffset - offset; if (isOpaque) { Win32.RECT rect = new Win32.RECT((int)fx1, y1, (int)fx2, y2); unsafe { fixed(char *p = &_text[offset]) { Win32.ExtTextOut(hdc, rect.left, rect.top, Win32.ETO_OPAQUE, &rect, p, displayLength, null); } } } else { unsafe { fixed(char *p = &_text[offset]) { Win32.ExtTextOut(hdc, (int)fx1, y1, 0, null, p, displayLength, null); } } } } if (dec.Underline) { DrawUnderline(hdc, foreColorRef, (int)fx1, y2 - 1, (int)fx2 - (int)fx1); } fx1 = fx2; word = word.Next; } }
/// <summary> /// Invert text attribute at the specified position. /// </summary> /// <remarks> /// <para>If doInvert was false, only splitting of GWords will be performed. /// It is required to avoid problem when the text which conatins blinking cursor is drawn by DrawWord().</para> /// <para>DrawWord() draws contiguous characters at once, /// and the character pitch depends how the character in the font was designed.</para> /// <para>By split GWord even if inversion is not required, /// the position of a character of the blinking cursor will be constant.</para> /// </remarks> /// <param name="index">Column index to invert.</param> /// <param name="doInvert">Whether inversion is really applied.</param> /// <param name="color">Background color of the inverted character.</param> internal void InvertCharacter(int index, bool doInvert, Color color) { //先にデータのあるところより先の位置を指定されたらバッファを広げておく if (index >= _displayLength) { int prevLength = _displayLength; ExpandBuffer(index + 1); for (int i = prevLength; i < index + 1; i++) { _text[i] = ' '; } _displayLength = index + 1; this.LastWord.Next = new GWord(TextDecoration.Default, prevLength, CharGroup.LatinHankaku); } if (_text[index] == WIDECHAR_PAD) { index--; } GWord prev = null; GWord word = _firstWord; int nextoffset = 0; while (word != null) { nextoffset = GetNextOffset(word); if (word.Offset <= index && index < nextoffset) { GWord next = word.Next; //キャレットの反転 TextDecoration inv_dec = word.Decoration; if (doInvert) { inv_dec = inv_dec.GetInvertedCopyForCaret(color); } //GWordは最大3つ(head:indexの前、middle:index、tail:indexの次)に分割される GWord head = word.Offset < index ? new GWord(word.Decoration, word.Offset, word.CharGroup) : null; GWord mid = new GWord(inv_dec, index, word.CharGroup); int nextIndex = index + CharGroupUtil.GetColumnsPerCharacter(word.CharGroup); GWord tail = nextIndex < nextoffset ? new GWord(word.Decoration, nextIndex, word.CharGroup) : null; //連結 head,tailはnullのこともあるのでややこしい List <GWord> list = new List <GWord>(3); if (head != null) { list.Add(head); head.Next = mid; } list.Add(mid); mid.Next = tail == null ? next : tail; if (tail != null) { list.Add(tail); } //前後との連結 if (prev == null) { _firstWord = list[0]; } else { prev.Next = list[0]; } list[list.Count - 1].Next = next; break; } prev = word; word = word.Next; } }
/// <summary> /// <ja> /// 指定位置に1文字書き込みます。 /// </ja> /// <en> /// Write one character to specified position. /// </en> /// </summary> /// <param name="ch"><ja>書き込む文字</ja><en>Character to write.</en></param> /// <param name="dec"><ja>テキスト書式を指定するTextDecorationオブジェクト</ja> /// <en>TextDecoration object that specifies text format /// </en></param> public void PutChar(char ch, TextDecoration dec) { Debug.Assert(dec != null); Debug.Assert(_caretColumn >= 0); Debug.Assert(_caretColumn < _text.Length); char originalChar = Unicode.ToOriginal(ch); CharGroup charGroup = Unicode.GetCharGroup(ch); bool onZenkaku = (_attrs[_caretColumn].CharGroup == CharGroup.CJKZenkaku); bool onZenkakuRight = (_text[_caretColumn] == GLine.WIDECHAR_PAD); if (onZenkaku) { //全角の上に書く if (!onZenkakuRight) { _text[_caretColumn] = originalChar; _attrs[_caretColumn] = new CharAttr(dec, charGroup); if (CharGroupUtil.GetColumnsPerCharacter(charGroup) == 1) { //全角の上に半角を書いた場合、隣にスペースを入れないと表示が乱れる _caretColumn++; if (_caretColumn < _text.Length) { _text[_caretColumn] = ' '; _attrs[_caretColumn].CharGroup = CharGroup.LatinHankaku; } } else { _attrs[_caretColumn + 1] = new CharAttr(dec, charGroup); _caretColumn += 2; } } else { _text[_caretColumn - 1] = ' '; _attrs[_caretColumn - 1].CharGroup = CharGroup.LatinHankaku; _text[_caretColumn] = originalChar; _attrs[_caretColumn] = new CharAttr(dec, charGroup); if (CharGroupUtil.GetColumnsPerCharacter(charGroup) == 2) { if (CharGroupUtil.GetColumnsPerCharacter(_attrs[_caretColumn + 1].CharGroup) == 2) { if (_caretColumn + 2 < _text.Length) { _text[_caretColumn + 2] = ' '; _attrs[_caretColumn + 2].CharGroup = CharGroup.LatinHankaku; } } _text[_caretColumn + 1] = GLine.WIDECHAR_PAD; _attrs[_caretColumn + 1] = _attrs[_caretColumn]; _caretColumn += 2; } else { _caretColumn++; } } } else //半角の上に書く { _text[_caretColumn] = originalChar; _attrs[_caretColumn] = new CharAttr(dec, charGroup); if (CharGroupUtil.GetColumnsPerCharacter(charGroup) == 2) { if (CharGroupUtil.GetColumnsPerCharacter(_attrs[_caretColumn + 1].CharGroup) == 2) //半角、全角となっているところに全角を書いたら { if (_caretColumn + 2 < _text.Length) { _text[_caretColumn + 2] = ' '; _attrs[_caretColumn + 2].CharGroup = CharGroup.LatinHankaku; } } _text[_caretColumn + 1] = GLine.WIDECHAR_PAD; _attrs[_caretColumn + 1] = _attrs[_caretColumn]; _caretColumn += 2; } else { _caretColumn++; //これが最もcommonなケースだが } } }