2009-05-23 2 views
16

Мы создаем документы PDF на лету из базы данных, используя PDFsharp.Рассчитать высоту текста на основе доступной ширины и шрифта?

Мне нужно знать, как лучше всего вычислить высоту текстовой области на основе на используемом шрифте и доступной ширине.

Мне нужно знать высоту, чтобы я мог обрабатывать разрывы страниц по мере необходимости.

ответ

10

В .NET можно назвать Graphics.MeasureString, чтобы узнать, как большой нарисованный текст будет.

Правильно, но при использовании PDFsharp вы вызываете XGraphics.MeasureString.

+1

Это не похоже на ответ. MeasureString не принимает прямоугольник/ширину и поэтому не может знать нарисованную высоту - или я что-то пропустил? – noelicus

+1

Используйте XGraphics.MeasureString, чтобы найти размеры одной строки текста. См. Класс XTextFormatter для кода, который автоматически разбивает строки (используя MeasureString()). Должно быть легко добавить новый метод XTextFormatter.MeasureString, который возвращает высоту. Обычно мы используем MigraDoc в нашем приложении, поэтому мы добавляем только текст в документ, а MigraDoc заботится о разрыве строк и разломах. –

20

Объект PdfSharp.Drawing.XGraphics имеет метод MeasureString, который возвращает то, что вам нужно.

var pdfDoc = new PdfSharp.Pdf.PdfDocument(); 
var pdfPage = pdfDoc.AddPage(); 
var pdfGfx = PdfSharp.Drawing.XGraphics.FromPdfPage(pdfPage); 
var pdfFont = new PdfSharp.Drawing.XFont("Helvetica", 20); 

while (pdfGfx.MeasureString("Hello World!").Width > pdfPage.Width) 
     --pdfFont.Size; 

pdfGfx.DrawString("Hello World!", pdfFont 
     , PdfSharp.Drawing.XBrushes.Black 
     , new PdfSharp.Drawing.XPoint(100, 100)); 

Это должно вам помочь. Пожалуйста, подумайте, что я не тестировал этот код, поскольку я написал его на лету, чтобы помочь. Он может содержать некоторые ошибки времени компиляции, но вы можете получить эту идею.

+0

Что такое класс «PdfSharp»? –

+0

@Chanipoz: Это не на самом деле класс, это приложение для n-уровня PDF, которое помогает создавать PDF-файлы на лету со стороны кода. –

0

ОП спросил, как рассчитать высоту текста на основе доступной ширины и шрифтом. Windows .NET предоставляет вызов API для этого, который принимает аргумент ширины; версия PDFsharp, которую я использую (0.9.653, .NET 1.1), нет.

Мое решение - используйте вызов .NET API с объектом Graphics, выделенным для объекта Bitmap, созданного пользователем, для получения ответа.

Что сработало для меня было использование растрового изображения с разрешением 100 DPI (критическое) и оказалось размером с портретную страницу (возможно, менее критическую).

Тогда я просто спросил .NET, какой размер пикселя будет для рисования на этом растровом изображении.

Возможно, вы захотите преобразовать единицы измерения с 1/100-й дюйма в точки (для PDFsharp).

 
''' Adapted Code - this not tested or even compiled - Caveat Emptor! 
''' Target: Visual Basic, .NET 1.1 (VS2003) [adapt as necessary] 

' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 
' GraphicsAlt.MeasureString() does substantially what System.Drawing MeasureString(...,Integer) does. 
' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 
Public Module GraphicsAlt 

    ' 
    ' Static data used Only to compute MeasureString() below. 
    ' 
    '  Cache a single copy of these two objects, to address an otherwise unexplained intermittent exception. 
    ' 
    Private Shared myImage As Bitmap = Nothing 
    Private Shared myGraphics As Graphics = Nothing 

    Public Shared Function GetMeasureGraphics() As Graphics 
     If myImage Is Nothing Then 
      myImage = New Bitmap(1700, 2200) '' ... Specify 8.5x11 
      myImage.SetResolution(100, 100) '' ... and 100 DPI (if you want different units, you might change this) 
      myGraphics = Graphics.FromImage(myImage) 
     End If 
     Return myGraphics 
    End Function 

    'Given 1/100TH inch max width, return Rect to hold with units 1/100TH inch 
    ' 
    Public Function MeasureString(ByVal text As String, ByVal aFont As System.Drawing.Font, ByVal width As Integer) As System.Drawing.SizeF 
     Return (GraphicsAlt.GetMeasureGraphics()).MeasureString(text, aFont, width) 
    End Function 

End Module 
+0

Это звучит как взломать, так как результаты могут отличаться от XGraphics.MeasureString, которые используются PDFsharp/MigraDoc из-за ошибок округления, кернинга, различной обработки полужирным шрифтом и курсивом. –

5

У меня была проблема, так похожую я реализовал этот метод расширения:

public static double MeasureHeight(this PdfSharp.Drawing.XGraphics gfx, string text, PdfSharp.Drawing.XFont font, int width) 
{ 
    var lines = text.Split('\n'); 

    double totalHeight = 0; 

    foreach (string line in lines) 
    { 
     var size = gfx.MeasureString(line, font); 
     double height = size.Height + (size.Height * Math.Floor(size.Width/width)); 

     totalHeight += height; 
    } 

    return totalHeight; 
} 
+0

Это работало хорошо для меня как быстрое и простое решение. Однако я заметил, что при выполнении size.Height * Math.Floor (size.Width/width) иногда будет недооцениваться ширина (или, может быть, TextFormatter переоценивает ширину при обертке текста). Поэтому я должен был увеличить значение ширины примерно на 15%, чтобы сделать его более точным. Не уверен, почему, если кто-нибудь может объяснить, что было бы полезно. –

0

В случае, если кто-то еще хочет, чтобы найти ответ, я реализовал достаточно простой для понимания способ найти из высоты полученного текста.

Public Function PrintString(text As String, ft As XFont, rect As XRect, graph As XGraphics, b As SolidBrush, Optional tf As XTextFormatter = Nothing) As Integer 
    If Not IsNothing(tf) Then 
     tf.DrawString(text, ft, b, rect) 
    Else 
     Dim drawLeft As New XStringFormat 
     drawLeft.Alignment = XStringAlignment.Near 

     graph.DrawString(text, ft, b, rect, drawLeft) 
    End If 

    Dim width As Double = graph.MeasureString(text, ft).Width 
    Dim multiplier As Integer = 0 

    While width > 0 
     multiplier += 1 

     width -= rect.Width 
    End While 

    Dim height As Double = (graph.MeasureString(text, ft).Height) * multiplier 
    Return height 
End Function 

Объясняя код:

Во-первых, печатать текст. Я включил необязательный XTextFormatter, называемый tf, потому что я использую XGraphics или XTextFormatters взаимозаменяемо в моем приложении.

Затем вычислите, сколько времени было проведено с помощью MeasureString(). Ширина.

Затем вычислите, сколько строк текста было.Это делается путем деления общей длины найденного ранее текста на ширину предоставленного прямоугольника (поле), где печатается налог. Я сделал это с циклом while здесь.

Умножьте высоту текста (используя graph.MeasureString(). Height) по количеству строк. Это конечная высота вашего текста.

Верните значение высоты. Теперь вызов функции PrintString() будет распечатывать текст, предоставленный после возвращения высоты печатного текста.

+0

Код не учитывает, что XTextFormatter добавляет разрывы строк только между словами. Результат PrintString может быть отключен одним или несколькими строками. Исходный код для класса XTextFormatter включен в PDFsharp. Этот класс предназначен для запуска людей, и вы можете добавить разрывы страниц или параметр out, который возвращает высоту. Цикл While можно заменить делением (но это не улучшит результаты). –

+0

@PDFsharpTeam Мне очень хотелось бы знать, как это сделать. Я всегда задавался вопросом, почему класс XTextFormatter автоматически не возвращает высоту полученного текста. Как отредактировать класс? – Wakka02

+0

@ Wakka02 Я создал класс XTextFormatterEx на основе XTextFormatter. Редактировать класс было легко, но у меня были некоторые технические проблемы, чтобы заставить его скомпилировать. Полный исходный код можно найти на форуме PDFsharp. См. Мой ответ на этот вопрос. –

0

PDFsharp включает в себя класс XTextFormatter, который может использоваться для рисования текста с помощью разрывов строк.

Однако он не может определить высоту, необходимую для текста. Вдохновленный комментарием от @ Wakka02, я улучшил этот класс, создав класс XTextFormatterEx.
По-моему, это также отвечает на исходный вопрос, поэтому я отправляю ответ.
Я знаю, что это старый вопрос, и ответ может не помочь ОП, но это часто задаваемый вопрос, и ответ может помочь другим.

Новый класс имеет 500 строк кода - и я думаю, что это было бы слишком много для этого сообщения.

Исходный код можно найти на PDFsharp форуме:
http://forum.pdfsharp.net/viewtopic.php?p=9213#p9213

Это также можно найти в моем скромном блоге:
http://developer.th-soft.com/developer/pdfsharp-improving-the-xtextformatter-class-measuring-the-height-of-the-text/

При использовании нового класса, вы можете сначала позвонить PrepareDrawString чтобы узнать, какая часть текста подходит и какая высота соответствует тексту. Затем ваш декодер может нарисовать подготовленный текст или подготовить другой текст или подготовить тот же текст с другим прямоугольником.

Мой новый класс на работе: XTextFormatterEx tf = new XTextFormatterEx (gfx); int lastCharIndex; double neededHeight;

// Draw the text in a box with the optimal height 
// (magic: we know that one page is enough). 
XRect rect = new XRect(40, 100, 250, double.MaxValue); 
//tf.Alignment = ParagraphAlignment.Left; 
tf.PrepareDrawString(text, font, rect, 
        out lastCharIndex, out neededHeight); 
rect = new XRect(40, 100, 250, neededHeight); 
gfx.DrawRectangle(XBrushes.SeaShell, rect); 
// Both variants should look the same. 

// Optimized version: draw the prepared string. 
tf.DrawString(XBrushes.Black, XStringFormats.TopLeft); 

Подготовка текста многократно вызывает MeasureString. Позже подготовленный текст можно рисовать без повторного вызова MeasureString.

На сегодняшний день (Juli 17, 2015) класс XTextFormatterEx (как и исходный XTextFormatter) использует внутренние поля класса XFont. Это требует специального лечения при компиляции класса. Я решил скопировать мой класс XTextFormatterEx в папку PDFsharp после загрузки полного исходного пакета для PDFsharp 1.32.
Любой, кто пытается изменить класс XTextFormatter или XTextFormatterEx, столкнется с той же проблемой.
Я надеюсь, что эта проблема будет решена с будущими версиями PDFsharp, позволяя включить модифицированные версии этих классов в проект приложения.

Смежные вопросы