2010-01-25 8 views
5

Vấn đề của tôi:

Tôi hiện đang làm việc trên điều khiển người dùng tùy chỉnh hiển thị các đoạn văn bản (mỗi phông chữ có phông chữ khác nhau) trên một dòng. Tôi muốn căn chỉnh tất cả các bit của văn bản chính xác dọc theo đường cơ sở chung. Ví dụ:Sử dụng GDI +, cách tiếp cận đơn giản nhất để căn chỉnh văn bản (được vẽ bằng nhiều phông chữ khác nhau) dọc theo đường cơ sở chung là gì?

Hello, I am George. 
------------------------------ <- all text aligns to a common baseline 
    ^  ^ ^
    |   |  | 
Courier Arial Times  <- font used for a particular bit of text 
    20pt  40pt  30pt 

Vì tôi không tìm thấy bất kỳ chức năng GDI + nào để thực hiện việc này trực tiếp, tôi đã đưa ra phương pháp riêng của mình (được nêu bên dưới). Tuy nhiên:

Tôi tự hỏi có thực sự không phải là cách dễ dàng hơn để hoàn thành công việc này không?

cách tiếp cận hiện tại của tôi:

1) Thu thập một danh sách tất cả System.Drawing.Font s sẽ được sử dụng để vẽ văn bản.

2) Đối với mỗi Font, tìm ra vị trí thẳng đứng của đường cơ sở tính theo pixel, sử dụng đoạn mã sau:

// variables used in code sample (already set) 
Graphics G; 
Font font; 
... 

// compute ratio in order to convert from font design units to pixels: 
var designUnitsPerPixel = font.GetHeight(G)/
          font.FontFamily.GetLineSpacing(font.Style); 

// get the cell ascent (baseline) position in design units: 
var cellAscentInDesignUnits = font.FontFamily.GetCellAscent(font.Style); 

// finally, convert the baseline position to pixels: 
var baseLineInPixels = cellAscentInDesignUnits * designUnitsPerPixel; 

3) Đối với tất cả Font s sử dụng, xác định giá trị lớn nhất baseLineInPixels như được tính ở trên và lưu trữ giá trị này thành maxBaseLineInPixels.

4) Vẽ mỗi bit của văn bản trong cách thức sau đây:

// variables used in code sample (already set): 
Graphics G; 
Font font; 
string text; 
... 

// find out how much space is needed for drawing the text 
var measureF = G.MeasureString(text, font); 

// determine location where text will be drawn: 
var layoutRectF = new RectangleF(new PointF(0f, 0f), measureF); 
layoutRectF.Y += maxBaseLineInPixels - baseLineInPixels; 
//^the latter value 'baseLineInPixels' is specific to the font used 

// draw text at specified location 
G.DrawString(text, font, Brushed.Black, layoutRectF); 

Tôi có thiếu cái gì đó, hoặc là có thực sự không có cách nào dễ dàng hơn?

+1

Đã trải qua ngày hôm nay và hôm qua. Dường như là giải pháp duy nhất. Lưu ý rằng Font.GetHeight (Graphics) sẽ trả về giá trị trong PageUnit của đối tượng Graphics, thay vì theo pixel. Tôi đang sử dụng giải pháp của Bob Powell (http://www.bobpowell.net/formattingtext.htm) ngay bây giờ, bằng cách sử dụng khối mã cuối cùng trong bài viết của mình (tốt, trong C#), và chuyển đổi thành mm, vì tôi cần điều này cho một thói quen tạo bản vẽ/in/bản vẽ được chia sẻ. – OregonGhost

+0

Cảm ơn phản hồi của bạn. Tôi cảm thấy nhẹ nhõm khi nghe (vâng ... :) rằng vẽ văn bản với GDI + thực sự * là * phức tạp và tôi không chỉ tưởng tượng ra điều này. Tôi đã quyết định từ bỏ vấn đề này và tiếp tục tới WPF (nơi GDI + không còn quan trọng và mọi thứ hy vọng sẽ dễ dàng hơn một chút). – stakx

Trả lời

0

Tôi nghĩ cách này hoạt động, vui lòng thử.

List<RectangleF> rects = new List<RectangleF>(); 

private void Form1_Paint(object sender, PaintEventArgs e) 
{ 
    ////////////////////Not Set baseLine 
    //baseline 
    e.Graphics.DrawLine(Pens.Red , new Point(100,200),new Point(800,200)); 

    //words 
    Point point = new Point(100,100); 
    e.Graphics.DrawString("hello world", new Font("Times", 30), Brushes.Black, point); 
    RectangleF rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Times", 30))); 
    e.Graphics.DrawRectangle(Pens.Green,rectangleF.X ,rectangleF.Y , rectangleF.Width , rectangleF.Height); 
    rects.Add(rectangleF); 

    point = new Point(400, 100); 
    e.Graphics.DrawString("hello world", new Font("Arial", 40), Brushes.Black, point); 
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Arial", 40))); 
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height); 
    rects.Add(rectangleF); 

    point = new Point(800, 100); 
    e.Graphics.DrawString("hello world", new Font("Courier", 20), Brushes.Black, point); 
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Courier", 20))); 
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height); 
    rects.Add(rectangleF); 

    ///////////////////SetBaseLine///////////////////////////// 
    var maxHeight = GetMaxHeight(); 
    /////////////////// 

    //baseLine 
    e.Graphics.DrawLine(Pens.Pink, new Point(100, (int) (400 + maxHeight/2)), new Point(800, (int) (400 + maxHeight/2))); 

    StringFormat stringFormat = new StringFormat(); 
    stringFormat.LineAlignment = StringAlignment.Center; 

    //words 
    point = new Point(100, 400); 
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Times", 30))); 
    e.Graphics.DrawString("hello world", new Font("Times", 30), Brushes.Black, new RectangleF(rectangleF.X ,rectangleF.Y , rectangleF.Width , maxHeight) , stringFormat); 
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height); 
    rects.Add(rectangleF); 

    point = new Point(400, 400); 
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Arial", 40))); 
    e.Graphics.DrawString("hello world", new Font("Arial", 40), Brushes.Black, new RectangleF(rectangleF.X, rectangleF.Y, rectangleF.Width, maxHeight), stringFormat); 
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height); 
    rects.Add(rectangleF); 

    point = new Point(800, 400); 
    rectangleF = new RectangleF(point, e.Graphics.MeasureString("hello world", new Font("Courier", 20))); 
    e.Graphics.DrawString("hello world", new Font("Courier", 20), Brushes.Black, new RectangleF(rectangleF.X, rectangleF.Y, rectangleF.Width, maxHeight), stringFormat); 
    e.Graphics.DrawRectangle(Pens.Green, rectangleF.X, rectangleF.Y, rectangleF.Width, rectangleF.Height); 
    rects.Add(rectangleF); 

} 

private float GetMaxHeight() 
{ 
    float temp = 0; 
    foreach (RectangleF rectangleF in rects) 
     if (rectangleF.Height > temp) 
      temp = rectangleF.Height; 

    return temp; 
} 
+0

Cảm ơn bạn đã đăng bài. Tuy nhiên, giải pháp này là tiếc là không hữu ích ... xin lỗi. Điều này là vì hai lý do: ** 1. ** Tôi đã từ lâu từ bỏ về điều này (xem ngày đăng bài gốc) và quyết định chuyển sang WPF thay thế. ** 2. ** Tôi nghi ngờ rằng có một số cách tiếp cận tốt trong mã của bạn ('MeasureString',' GetMaxHeight'); Thật không may, hầu hết các mã còn lại cho thấy một cái gì đó khác, cụ thể là bạn đang làm việc hoàn toàn với các tọa độ cố định được tính toán trước và văn bản vẽ hai lần (có vẻ không cần thiết). Tuy nhiên, cảm ơn vì đã dành thời gian để trả lời. – stakx

0

Tôi đã nghiên cứu điều tương tự trong vài ngày qua và cuối cùng tôi đã tìm thấy câu trả lời on this blog page. Mã này (ở cuối bài viết) hoạt động thực sự tốt cho tôi và hy vọng giúp bất kỳ ai khác đấu tranh với vấn đề này:

private void DrawOnBaseline(string s, Graphics g, Font f, Brush b, Point pos) 
    { 
     float baselineOffset=f.SizeInPoints/f.FontFamily.GetEmHeight(f.Style)*f.FontFamily.GetCellAscent(f.Style); 
     float baselineOffsetPixels = g.DpiY/72f*baselineOffset; 

     g.DrawString(s,f,b,new Point(pos.X,pos.Y-(int)(baselineOffsetPixels+0.5f)),StringFormat.GenericTypographic); 
    }