Ответ 1
В вашем вопросе мало ясности относительно того, что такое "лучший" прямоугольник. Я предполагаю, что вы имеете в виду самый большой прямоугольник, который будет отображаться на 100% при печати.
Итак, давайте начнем с того, чтобы убедиться, что мы понимаем, что представляет собой графический объект документа печати "происхождение" и как свойство OriginAtMargins влияет на это происхождение.
OriginAtMargins - возвращает или задает значение, указывающее, будет ли позиция графического объекта, связанного со страницей, находится только внутри пользовательские поля или в верхнем левом углу области печатистраницы.
- Определение класса печатиDocument на MSDN
Итак, если OriginAtMargins
установлен на false
(по умолчанию), графический объект будет настроен на прямоугольник PrintableArea (около 5/32 от края страницы для моего лазерного принтера, старые лазерные принтеры могут быть больше, новые струйные принтеры могут печатайте справа на краю, программные принтеры PDF будут печататься прямо к краю). Таким образом, 0,0 в моем графическом объекте на самом деле составляет 16,16 на физической странице моего лазерного принтера (ваш принтер может быть другим).
Если поля по умолчанию 1 дюйм страницы и OriginAtMargins
установлены на true
, графический объект будет отрегулирован на прямоугольник 100,100,650,1100 для обычной портретной страницы. Это один дюйм внутри каждого физического края страницы. Таким образом, 0,0 в вашем графическом объекте фактически составляет 100 100 на физической странице.
Поля также известны как "мягкие поля", поскольку они определены в программном обеспечении и не зависят от физического устройства печати. Это означает, что они будут применены к текущему размеру страницы в программном обеспечении и отражают фактический портрет или альбомный размер страницы.
PrintableArea также известна как "жесткие поля", которые отражают физические ограничения вашего устройства печати. Это может варьироваться от принтера к принтеру, от производителя до производителя. Поскольку это аппаратные измерения, они не вращаются, когда вы устанавливаете страницу в альбомную или портретную. Физические ограничения не будут изменяться на принтере независимо от настроек печати программного обеспечения, поэтому мы должны убедиться, что мы применяем их на правильной оси в зависимости от наших параметров программного обеспечения для документа (ориентации) для печати.
Итак, следуя грубой модели кода примера, вы отправили здесь обработчик событий PrintDocument.PrintPage, который будет рисовать прямоугольник как можно больше, пока он все еще отображается (при этом PrintDocument.OriginsAtMargins
по умолчанию false
). Если вы установите PrintDocument.OriginsAtMargins
на true
, он нарисует прямоугольник как можно больше, пока он будет отображаться внутри настроенных программных полей (по умолчанию 1 на краях страницы).
PrintAction printAction = PrintAction.PrintToFile;
private void printDocument_BeginPrint(object sender, PrintEventArgs e)
{
// Save our print action so we know if we are printing
// a preview or a real document.
printAction = e.PrintAction;
// Set some preferences, our method should print a box with any
// combination of these properties being true/false.
printDocument.OriginAtMargins = false; //true = soft margins, false = hard margins
printDocument.DefaultPageSettings.Landscape = false;
}
private void printDocument_PrintPage(object sender, PrintPageEventArgs e)
{
Graphics g = e.Graphics;
// If you set printDocumet.OriginAtMargins to 'false' this event
// will print the largest rectangle your printer is physically
// capable of. This is often 1/8" - 1/4" from each page edge.
// ----------
// If you set printDocument.OriginAtMargins to 'false' this event
// will print the largest rectangle permitted by the currently
// configured page margins. By default the page margins are
// usually 1" from each page edge but can be configured by the end
// user or overridden in your code.
// (ex: printDocument.DefaultPageSettings.Margins)
// Grab a copy of our "soft margins" (configured printer settings)
// Defaults to 1 inch margins, but could be configured otherwise by
// the end user. You can also specify some default page margins in
// your printDocument.DefaultPageSetting properties.
RectangleF marginBounds = e.MarginBounds;
// Grab a copy of our "hard margins" (printer capabilities)
// This varies between printer models. Software printers like
// CutePDF will have no "physical limitations" and so will return
// the full page size 850,1100 for a letter page size.
RectangleF printableArea = e.PageSettings.PrintableArea;
// If we are print to a print preview control, the origin won't have
// been automatically adjusted for the printer physical limitations.
// So let adjust the origin for preview to reflect the printer
// hard margins.
if (printAction == PrintAction.PrintToPreview)
g.TranslateTransform(printableArea.X, printableArea.Y);
// Are we using soft margins or hard margins? Lets grab the correct
// width/height from either the soft/hard margin rectangles. The
// hard margins are usually a little wider than the soft margins.
// ----------
// Note: Margins are automatically applied to the rotated page size
// when the page is set to landscape, but physical hard margins are
// not (the printer is not physically rotating any mechanics inside,
// the paper still travels through the printer the same way. So we
// rotate in software for landscape)
int availableWidth = (int)Math.Floor(printDocument.OriginAtMargins
? marginBounds.Width
: (e.PageSettings.Landscape
? printableArea.Height
: printableArea.Width));
int availableHeight = (int)Math.Floor(printDocument.OriginAtMargins
? marginBounds.Height
: (e.PageSettings.Landscape
? printableArea.Width
: printableArea.Height));
// Draw our rectangle which will either be the soft margin rectangle
// or the hard margin (printer capabilities) rectangle.
// ----------
// Note: we adjust the width and height minus one as it is a zero,
// zero based co-ordinates system. This will put the rectangle just
// inside the available width and height.
g.DrawRectangle(Pens.Red, 0, 0, availableWidth - 1, availableHeight - 1);
}
Две строки, которые определяют доступную ширину и доступную высоту, я думаю, что вы искали в своем вопросе. Эти две линии учитывают, хотите ли вы мягкие поля или жесткие поля и задан ли печатный документ для пейзажа или портрета.
Я использовал Math.Floor()
для простого выхода, чтобы просто удалить что-нибудь за десятичным (например: 817.96 → 817), чтобы убедиться, что доступная ширина и высота находятся только в доступных размерах. Я здесь "не в безопасности", если вы хотите, чтобы вы могли поддерживать координаты на основе float (вместо int), просто будьте осторожны, чтобы наблюдать за ошибками округления, которые приведут к обрезанной графике (если она округляется с 817,96 до 818 а затем драйвер принтера решает, что больше не видно).
Я тестировал эту процедуру как в портретной, так и в альбомной ориентации с жесткими полями и мягкими полями на принтере Dell 3115CN, Samsung SCX-4x28 и CutePDF. Если это не удовлетворительно рассмотрит ваш вопрос, рассмотрите вопрос о пересмотре вашего вопроса, чтобы уточнить "волшебный прямоугольник" и "лучший прямоугольник".
EDIT: заметки о "мягких полях"
Мягкие поля применяются в программном обеспечении и не учитывают аппаратные ограничения принтера. Это намеренно и по дизайну. Вы можете установить мягкие поля вне области печати, если хотите, и выход может быть обрезан вашим драйвером принтера. Если это нежелательно для вашего приложения, вам необходимо настроить поля в программном коде. Либо вы можете запретить пользователю выбирать поля вне области печати (или предупредить их, если они это сделают), либо вы можете принудительно выполнить некоторые минимальные/максимальные условия в своем коде, когда вы действительно начнете печатать (рисовать) документ.
Пример:Если вы устанавливаете поля страницы в 0,0,0,0 в Microsoft Word 2007, появляется диалоговое окно с надписью "Одно или несколько полей устанавливаются за пределами области печати на странице". Нажмите кнопку "Исправить", чтобы увеличить соответствующие поля. "Если вы нажмете кнопку fix, Word просто скопирует жесткие поля в мягкие поля, поэтому в диалоговом окне теперь отображается 0,16" для всех полей (возможности моего лазерного принтера).
Это ожидаемое поведение. Это не ошибка/проблема с Microsoft Word, если печатная страница обрезается, потому что пользователь проигнорировал это предупреждение и использовал 0,0,0,0 полей страницы. Это то же самое в вашем приложении. Вам необходимо обеспечить соблюдение ограничений на все, что подходит в вашем случае использования. Либо с предупреждающим диалогом, либо вы можете усилить предел сильнее в коде (не предлагайте выбор пользователю).
Альтернативная стратегия
Хорошо, может быть, вы не хотите просто получать жесткие поля, а скорее получаете мягкие поля и затем принудительно устанавливаете, чтобы при печати печатались мягкие поля внутри области печати. Здесь можно разработать другую стратегию.
В этом примере я буду использовать исходники с полями и разрешить пользователю выбирать любой желаемый марж, но я собираюсь ввести в код код, чтобы выбранное поле не было вне области печати. Если выбранные поля находятся за пределами области печати, я просто собираюсь настроить их внутри области печати.
PrintAction printAction = PrintAction.PrintToFile;
private void printDocument_BeginPrint(object sender, PrintEventArgs e)
{
// Save our print action so we know if we are printing
// a preview or a real document.
printAction = e.PrintAction;
// We ALWAYS want true here, as we will implement the
// margin limitations later in code.
printDocument.OriginAtMargins = true;
// Set some preferences, our method should print a box with any
// combination of these properties being true/false.
printDocument.DefaultPageSettings.Landscape = false;
printDocument.DefaultPageSettings.Margins.Top = 100;
printDocument.DefaultPageSettings.Margins.Left = 0;
printDocument.DefaultPageSettings.Margins.Right = 50;
printDocument.DefaultPageSettings.Margins.Bottom = 0;
}
private void printDocument_PrintPage(object sender, PrintPageEventArgs e)
{
Graphics g = e.Graphics;
// If you set printDocumet.OriginAtMargins to 'false' this event
// will print the largest rectangle your printer is physically
// capable of. This is often 1/8" - 1/4" from each page edge.
// ----------
// If you set printDocument.OriginAtMargins to 'false' this event
// will print the largest rectangle permitted by the currently
// configured page margins. By default the page margins are
// usually 1" from each page edge but can be configured by the end
// user or overridden in your code.
// (ex: printDocument.DefaultPageSettings.Margins)
// Grab a copy of our "hard margins" (printer capabilities)
// This varies between printer models. Software printers like
// CutePDF will have no "physical limitations" and so will return
// the full page size 850,1100 for a letter page size.
RectangleF printableArea = e.PageSettings.PrintableArea;
RectangleF realPrintableArea = new RectangleF(
(e.PageSettings.Landscape ? printableArea.Y : printableArea.X),
(e.PageSettings.Landscape ? printableArea.X : printableArea.Y),
(e.PageSettings.Landscape ? printableArea.Height : printableArea.Width),
(e.PageSettings.Landscape ? printableArea.Width : printableArea.Height)
);
// If we are printing to a print preview control, the origin won't have
// been automatically adjusted for the printer physical limitations.
// So let adjust the origin for preview to reflect the printer
// hard margins.
// ----------
// Otherwise if we really are printing, just use the soft margins.
g.TranslateTransform(
((printAction == PrintAction.PrintToPreview)
? realPrintableArea.X : 0) - e.MarginBounds.X,
((printAction == PrintAction.PrintToPreview)
? realPrintableArea.Y : 0) - e.MarginBounds.Y
);
// Draw the printable area rectangle in PURPLE
Rectangle printedPrintableArea = Rectangle.Truncate(realPrintableArea);
printedPrintableArea.Width--;
printedPrintableArea.Height--;
g.DrawRectangle(Pens.Purple, printedPrintableArea);
// Grab a copy of our "soft margins" (configured printer settings)
// Defaults to 1 inch margins, but could be configured otherwise by
// the end user. You can also specify some default page margins in
// your printDocument.DefaultPageSetting properties.
RectangleF marginBounds = e.MarginBounds;
// This intersects the desired margins with the printable area rectangle.
// If the margins go outside the printable area on any edge, it will be
// brought in to the appropriate printable area.
marginBounds.Intersect(realPrintableArea);
// Draw the margin rectangle in RED
Rectangle printedMarginArea = Rectangle.Truncate(marginBounds);
printedMarginArea.Width--;
printedMarginArea.Height--;
g.DrawRectangle(Pens.Red, printedMarginArea);
}