Рендеринг подписки с использованием DrawText или аналогичной функции - PullRequest
4 голосов
/ 20 февраля 2012

Вопрос прост. Как нарисовать следующий текст в ячейке TStringGrid?

enter image description here

Операционная система - Windows XP (или Windows Vista или Windows 7). Предпочтительной средой разработки является C ++ Builder 6, но я также приму решения для C ++ Builder XE из Delphi. Предпочтительная функция API - DrawText, но если существует лучшая функция, то это не проблема. Имя шрифта Times New Roman, размер шрифта 11. В настоящее время я использую этот метод для визуализации содержимого ячейки (упрощенно):

void __fastcall TForm_Main::StringGrid_DrawCell(TObject *Sender,
  int ACol, int ARow, TRect &Rect, TGridDrawState State)
{
   TStringGrid *grid = (TStringGrid*)Sender;
   if (grid == NULL) return;

   // 1. BACKGROUND
   grid->Canvas->Brush->Color = grid->Color;
   grid->Canvas->FillRect(Rect);

   // 2. TEXT
   grid->Canvas->Font->Assign(grid->Font);   // Times New Roman, 11pt

   // horizontal centering
   RECT RText = static_cast<RECT>(Rect);
   AnsiString text = grid->Cells[ACol][ARow];
   if (!text.IsEmpty()) {
      int text_len = strlen(text.c_str());
      SIZE size;
      memset(&size, 0, sizeof(SIZE));
      GetTextExtentPoint32(grid->Canvas->Handle, text.c_str(), text_len, &size);
      int offset_x = (Rect.Width() - size.cx) >> 1;
      RText.left += offset_x; RText.right += offset_x;

      // rendering
      DrawText(grid->Canvas->Handle, text.c_str(), text_len, &RText, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
   }
}

Некоторые символы имеют нижний индекс глифа в качестве специального символа Юникода (например, код 0x2081 назначен нижнему индексу), но, к сожалению, это не относится к заглавной букве U. Также эти символы поддерживаются не всеми шрифтами (например, шрифт Times New Roman) не поддерживает диапазон кода 0x2070 - 209F), см. эту статью в Википедии. Я ищу более общее решение, подобное реализованному в Microsoft Word. В MS Word нет проблем с отображением заглавной буквы U в качестве индекса с использованием шрифта Times New Roman.

1 Ответ

0 голосов
/ 07 января 2013

Если вы хотите, чтобы какой-то символ отображался как верхний индекс, вы должны поставить перед ним префикс ^. Точно так же символы нижнего индекса должны иметь префикс _.

void __fastcall TForm_Main::StringGrid_DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State)
{
   TStringGrid *grid = (TStringGrid*)Sender;
   if (grid == NULL)
   {
      return;
   }
   WideString wtext = L"φ_U = 120";
   if (wtext.IsEmpty()) return;

   // layout
   SIZE size;
   memset(&size, 0, sizeof(SIZE));
   SSGetTextExtentPoint(grid->Canvas, wtext, size);
   int offset_x = (Rect.Width() - size.cx + 1) >> 1;  // horizontal centering
   RECT RText = static_cast<RECT>(Rect);
   RText.left += offset_x;
   RText.right += offset_x;

   // rendering
   SetBkMode(grid->Canvas->Handle, TRANSPARENT);
   SSDrawText(grid->Canvas, wtext, RText, DT_LEFT);
}

int TForm_Main::SSGetTextExtentPoint(TCanvas *canvas, WideString text, SIZE &size)
{
   // Source: http://www.codeproject.com/Articles/12660/Using-Subscripts-and-Superscripts-When-Showing-Tex
   SaveDC(canvas->Handle);

   SIZE sz;
   RECT outRect =
   {0, 0, 0, 0};

   HFONT oldFont;

   LOGFONT lf;
   GetObject(canvas->Font->Handle, sizeof(LOGFONT), &lf);

   POINT sub, sup, subofs, supofs;

   // Calculate subscript/superscript size and offsets
   bool use_pixel_unit = false;
   if (lf.lfHeight < 0)
   {
      lf.lfHeight    = MulDiv(-lf.lfHeight, 72, GetDeviceCaps(canvas->Handle, LOGPIXELSY));
      use_pixel_unit = true;
   }
   sub.x = lf.lfWidth / 2;
   sup.x = lf.lfWidth / 2;
   sub.y = lf.lfHeight / 3 * 2;
   sup.y = lf.lfHeight / 3 * 2;

   subofs.x = lf.lfWidth / 2;
   supofs.x = lf.lfWidth / 2;
   subofs.y = lf.lfHeight / 6;
   supofs.y = lf.lfHeight / 3;

   lf.lfWidth  = sub.x;
   lf.lfHeight = sub.y;
   if (use_pixel_unit)
   {
      lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(canvas->Handle, LOGPIXELSY), 72);
   }
   HFONT SubFont;
   SubFont = CreateFontIndirect(&lf);

   lf.lfWidth  = sup.x;
   lf.lfHeight = sup.y;
   HFONT SupFont;
   if (use_pixel_unit)
   {
      lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(canvas->Handle, LOGPIXELSY), 72);
   }
   SupFont = CreateFontIndirect(&lf);

   WideString temp = text;
   TCHAR c;

   // Calculate the size of the text that needs to be displayed
   do
   {
      int x1       = 1, x2 = 1, x = 1;
      WideString s = "";
      c            = ' ';
      bool bFind   = true;

      // Find the first "^" or "_", indicating the sub- or superscript
      while (bFind)
      {
         x1 = text.Pos(L"^");
         x2 = text.Pos(L"_");
         if ((x1 == 0) && (x2 == 0))
         {
            x = 0;
         }
         else if ((x1 > 0) && (x2 > 0))
         {
            x = min(x1, x2);
         }
         else if (x1 > 0)
         {
            x = x1;
         }
         else
         {
            x = x2;
         }
         if (x == 0)
         {
            bFind = false;
            x     = text.Length() + 1;
         }
         else if (x == text.Length())
         {
            bFind = false;
         }
         else if (text[x] != text[x + 1])
         {
            bFind = false;
            c     = text[x];
         }
         else
         {
         x++;
         }
         s = s + text.SubString(1, x - 1);
         text.Delete(1, min(x, text.Length()));
      }
      sz = canvas->TextExtent(s);
      outRect.right += sz.cx;
      if ((outRect.bottom - outRect.top) < sz.cy)
      {
         outRect.top = outRect.bottom - sz.cy;
      }

      switch (c)
      {
      case '^':
         oldFont = (HFONT)SelectObject(canvas->Handle, SupFont);
         GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
         outRect.right += sz.cx + supofs.x;
         text.Delete(1, 1);
         SelectObject(canvas->Handle, oldFont);
         break;
      case '_':
         oldFont = (HFONT)SelectObject(canvas->Handle, SubFont);
         GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
         outRect.right += sz.cx + subofs.x;
         text.Delete(1, 1);
         SelectObject(canvas->Handle, oldFont);
         break;
      }
   }
   while (c != ' ');

   // Adjust text position
   outRect.bottom += subofs.y;
   outRect.top -= subofs.x;

   size.cx = outRect.right - outRect.left;
   size.cy = outRect.bottom - outRect.top;

   DeleteObject(SubFont);
   DeleteObject(SupFont);
   // Done, restoring the device context
   RestoreDC(canvas->Handle, -1);
   return 0;
}

// ---------------------------------------------------------------------------
int TForm_Main::SSDrawText(TCanvas *canvas, WideString text, RECT &drawRect, int justification)
{
   // Source: http://www.codeproject.com/Articles/12660/Using-Subscripts-and-Superscripts-When-Showing-Tex
   SaveDC(canvas->Handle);

   SIZE sz;
   RECT outRect =
   {0, 0, 0, 0};

   HFONT oldFont;
   LOGFONT lf;
   GetObject(canvas->Font->Handle, sizeof(LOGFONT), &lf);

   POINT sub, sup, subofs, supofs;
   bool contains_subscript   = false;
   bool contains_superscript = false;

   // Calculate subscript/superscript size and offsets
   bool use_pixel_unit = false;
   if (lf.lfHeight < 0)
   {
      lf.lfHeight    = MulDiv(-lf.lfHeight, 72, GetDeviceCaps(canvas->Handle, LOGPIXELSY));
      use_pixel_unit = true;
   }

   sub.x = (lf.lfWidth + 1) >> 1;
   sup.x = (lf.lfWidth + 1) >> 1;
   sub.y = (lf.lfHeight << 1) / 3;
   sup.y = (lf.lfHeight << 1) / 3;
   if (lf.lfHeight == 10)
   {
      sub.y++; // make subscript a little larger
   }

   subofs.x = (lf.lfWidth + 1) >> 1;
   supofs.x = (lf.lfWidth + 1) >> 1;
   subofs.y = (lf.lfHeight + 3) / 6;
   supofs.y = (lf.lfHeight) / 3;

   long sub_shift_down = lf.lfHeight - sub.y;

   lf.lfWidth  = sub.x;
   lf.lfHeight = sub.y;
   if (use_pixel_unit)
   {
      lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(canvas->Handle, LOGPIXELSY), 72);
   }
   HFONT SubFont;
   SubFont = CreateFontIndirect(&lf);

   lf.lfWidth  = sup.x;
   lf.lfHeight = sup.y;
   if (use_pixel_unit)
   {
      lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(canvas->Handle, LOGPIXELSY), 72);
   }
   HFONT SupFont;
   SupFont = CreateFontIndirect(&lf);

   WideString temp = text;
   TCHAR c;

   // Calculate the size of the text that needs to be displayed
   do
   {
      int x1       = 1, x2 = 1, x = 1;
      WideString s = "";
      c            = ' ';
      bool bFind   = true;

      // Find the first "^" or "_", indicating the sub- or superscript
      while (bFind)
      {
         x1 = text.Pos(L"^");
         x2 = text.Pos(L"_");
         if ((x1 == 0) && (x2 == 0))
         {
            x = 0;
         }
         else if ((x1 > 0) && (x2 > 0))
         {
            x = min(x1, x2);
         }
         else if (x1 > 0)
         {
            x = x1;
         }
         else
       {
            x = x2;
         }
         if (x == 0)
         {
            bFind = false;
            x     = text.Length() + 1;
         }
         else if (x == text.Length())
         {
            bFind = false;
         }
         else if (text[x] != text[x + 1])
         {
            bFind = false;
            c     = text[x];
         }
         else
         {
            x++;
         }
         s = s + text.SubString(1, x - 1);
         text.Delete(1, min(x, text.Length()));
      }
      sz = canvas->TextExtent(s);
      outRect.right += sz.cx;
      if ((outRect.bottom - outRect.top) < sz.cy)
      {
         outRect.top = outRect.bottom - sz.cy;
      }

      switch (c)
      {
      case '^':
         oldFont = (HFONT)SelectObject(canvas->Handle, SupFont);
         GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
         outRect.right += sz.cx + supofs.x;
         text.Delete(1, 1);
         SelectObject(canvas->Handle, oldFont);
         contains_superscript = true;
         break;
      case '_':
         oldFont = (HFONT)SelectObject(canvas->Handle, SubFont);
         GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
         outRect.right += sz.cx + subofs.x;
       text.Delete(1, 1);
         SelectObject(canvas->Handle, oldFont);
         contains_subscript = true;
         break;
      }
   }
   while (c != ' ');

   // Adjust text position
   if (contains_subscript)
   {
      outRect.bottom += subofs.y;
   }
   if (contains_superscript)
   {
      outRect.top -= supofs.y;
   }
   POINT Origin;
   Origin.y = drawRect.top + (((drawRect.bottom - drawRect.top) - (outRect.bottom - outRect.top) + 1) >> 1);

   switch (justification)
   {
   case DT_CENTER:
     Origin.x = (drawRect.right - drawRect.left) / 2 - (outRect.right - outRect.left) / 2 + drawRect.left;
     break;
   case DT_LEFT:
     Origin.x = drawRect.left;
     break;
   case DT_RIGHT:
     Origin.x = drawRect.right - (outRect.right - outRect.left);
   }

   POINT pnt = Origin;

   text = temp;

   // Draw text
   do
   {
      int x1       = 1, x2 = 1, x = 1;
      WideString s = "";
      c            = ' ';
      bool bFind   = true;

      // Find the first "^" or "_", indicating the sub- or superscript
     while (bFind)
      {
         x  = text.Pos(L"^_");
         x1 = text.Pos(L"^");
         x2 = text.Pos(L"_");
         if ((x1 == 0) && (x2 == 0))
         {
            x = 0;
         }
         else if ((x1 > 0) && (x2 > 0))
         {
            x = min(x1, x2);
         }
         else if (x1 > 0)
         {
            x = x1;
         }
         else
         {
            x = x2;
         }
         if (x == 0)
         {
            bFind = false;
            x     = text.Length() + 1;
         }
         else if (x == text.Length())
         {
            bFind = false;
         }
         else if (text[x] != text[x + 1])
         {
            bFind = false;
            c     = text[x];
         }
         else
         {
            x++;
         }
         s = s + text.SubString(1, x - 1);
         text.Delete(1, min(x, text.Length()));
      }
      // Draw main text
      ExtTextOutW(canvas->Handle, pnt.x, pnt.y, 0, &drawRect, s.c_bstr(), s.Length(), NULL);
      GetTextExtentPoint32W(canvas->Handle, s.c_bstr(), s.Length(), &sz);
     pnt.x += sz.cx;

      // Draw subscript or superscript
      switch (c)
      {
      case '^':
         oldFont = (HFONT)SelectObject(canvas->Handle, SupFont);
         ExtTextOutW(canvas->Handle, pnt.x + supofs.x, pnt.y - supofs.y, 0, &drawRect, text.c_bstr(), 1, NULL);
         GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
         pnt.x += sz.cx + supofs.x;
         text.Delete(1, 1);
         SelectObject(canvas->Handle, oldFont);
         break;
      case '_':
         oldFont = (HFONT)SelectObject(canvas->Handle, SubFont);
         ExtTextOutW(canvas->Handle, pnt.x + subofs.x, pnt.y + subofs.y + sub_shift_down, 0, &drawRect, text.c_bstr(), 1, NULL);
         GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
         pnt.x += sz.cx + subofs.x;
         text.Delete(1, 1);
         SelectObject(canvas->Handle, oldFont);
         break;
      }
   }
   while (c != ' ');

   DeleteObject(SubFont);
   DeleteObject(SupFont);
   // Done, restoring the device context
   RestoreDC(canvas->Handle, -1);
   return 0;
}
...