windows - Rendering subscript ved hjælp af DrawText eller lignende funktion

Indlæg af Hanne Mølgaard Plasc

Problem



Spørgsmålet er simpelt. Hvordan tegnes følgende tekst i TStringGrid celle?


Indtast billedbeskrivelse her


Operativsystem er Windows XP (eller Windows Vista eller Windows 7). Foretrukket udviklingsmiljø er C ++ Builder 6, men jeg vil også acceptere løsninger til C ++ Builder XE of Delphi. Foretrukken API-funktion er DrawText, men hvis der findes bedre funktion end dette ikke noget problem. Skriftnavn er Times New Roman, skrifttypestørrelse er 11. I øjeblikket bruger jeg denne metode til at gøre celleindhold (forenklet):


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);
   }
}


Nogle tegn har subscript glyph som special unicode tegn (for eksempel kode 0x2081 er tildelt til abonnement en) men desværre er det ikke tilfældet med bogstavet U. Også disse tegn understøttes ikke af alle skrifttyper (for eksempel Times New Roman skrifttype understøtter kode område 0x2070 - 209F), se denne Wikipedia artikel. Jeg søger efter en mere generel løsning som dem, der implementeres af Microsoft Word. MS Word har ikke problemer med at gøre hovedbog U som underskrift ved brug af Times New Roman skrifttype. [5]

Bedste reference


Hvis du vil have nogle char til at gøre som superskript, skal du prefikse det med ^. På samme måde skal subscript-tegn være præfikset med \_.


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;
}