500 lines
28 KiB
C#
500 lines
28 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Drawing;
|
||
using System.Drawing.Printing;
|
||
using System.Linq;
|
||
using System.Net.Http;
|
||
using System.Net.Http.Headers;
|
||
using System.Security.Cryptography; // Для шифрования/дешифрования
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using System.Threading.Tasks;
|
||
using System.Windows.Forms;
|
||
using Newtonsoft.Json;
|
||
using Newtonsoft.Json.Linq;
|
||
using SKLADm.Properties; // Для доступа к Settings.Default
|
||
using SKLADm;
|
||
using ZXing;
|
||
using ZXing.Common;
|
||
using ZXing.Windows.Compatibility;
|
||
|
||
namespace OzonInternalLabelPrinter // Убедитесь, что это ваше пространство имен
|
||
{
|
||
public partial class Form1 : MaterialSkin.Controls.MaterialForm // <-- Должно быть так
|
||
{
|
||
private HttpClient _httpClient;
|
||
// Bitmap больше не нужен как поле класса
|
||
// private Bitmap _labelBitmap;
|
||
private string _currentProductName;
|
||
private string _currentSku;
|
||
private string _currentBarcode;
|
||
private List<OzonStore> _availableStores;
|
||
// Поле для хранения экземпляра документа между настройкой и печатью
|
||
private PrintDocument _printDocForSetup; // Используем для PageSetup и Print
|
||
|
||
// Конструктор формы
|
||
public Form1()
|
||
{
|
||
InitializeComponent(); // Инициализирует компоненты из ДИЗАЙНЕРА
|
||
InitializeHttpClient();
|
||
// Инициализируем PrintDocument один раз
|
||
_printDocForSetup = new PrintDocument();
|
||
// Устанавливаем обработчик печати один раз
|
||
_printDocForSetup.PrintPage += new PrintPageEventHandler(pd_PrintPage);
|
||
// Загружаем магазины при запуске ПОСЛЕ инициализации _printDocForSetup
|
||
LoadStoresToComboBox();
|
||
}
|
||
|
||
// Инициализация HTTP клиента
|
||
private void InitializeHttpClient()
|
||
{
|
||
_httpClient = new HttpClient
|
||
{
|
||
BaseAddress = new Uri("https://api-seller.ozon.ru/")
|
||
};
|
||
_httpClient.DefaultRequestHeaders.Accept.Clear();
|
||
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||
}
|
||
|
||
// --- Логика работы с магазинами и ключами ---
|
||
|
||
private static readonly byte[] s_entropy = null; // Энтропия для шифрования
|
||
|
||
// Дешифрование Api-Key
|
||
private string DecryptApiKey(string encryptedKeyBase64)
|
||
{
|
||
if (string.IsNullOrEmpty(encryptedKeyBase64)) return string.Empty;
|
||
try
|
||
{
|
||
byte[] encryptedBytes = Convert.FromBase64String(encryptedKeyBase64);
|
||
byte[] decryptedBytes = ProtectedData.Unprotect(encryptedBytes, s_entropy, DataProtectionScope.CurrentUser);
|
||
return Encoding.UTF8.GetString(decryptedBytes);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"Ошибка дешифрования ключа: {ex.Message}");
|
||
MessageBox.Show($"Не удалось дешифровать Api-Key для магазина '{cmbStores.Text}'. Возможно, настройки повреждены или созданы под другим пользователем.\n\nОшибка: {ex.Message}",
|
||
"Ошибка ключа", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// Загрузка магазинов из настроек в ComboBox
|
||
private void LoadStoresToComboBox()
|
||
{
|
||
string json = Settings.Default.SavedStoresJson;
|
||
_availableStores = new List<OzonStore>();
|
||
if (!string.IsNullOrEmpty(json))
|
||
{
|
||
try
|
||
{
|
||
_availableStores = JsonConvert.DeserializeObject<List<OzonStore>>(json) ?? new List<OzonStore>();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"Ошибка загрузки списка магазинов: {ex.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
_availableStores = new List<OzonStore>();
|
||
}
|
||
}
|
||
|
||
cmbStores.DataSource = null;
|
||
cmbStores.DataSource = _availableStores;
|
||
cmbStores.DisplayMember = "StoreName";
|
||
|
||
bool storesExist = _availableStores.Any();
|
||
cmbStores.Enabled = storesExist;
|
||
txtOfferId.Enabled = storesExist;
|
||
btnPageSetup.Enabled = storesExist; // Включаем настройку, если есть магазины
|
||
// Кнопка GetData включится при вводе артикула
|
||
|
||
if (storesExist)
|
||
{
|
||
cmbStores.SelectedIndex = 0;
|
||
// Установим принтер по умолчанию для _printDocForSetup, если возможно
|
||
TrySetDefaultPrinterForDocument();
|
||
}
|
||
else
|
||
{
|
||
cmbStores.SelectedIndex = -1;
|
||
// Показываем сообщение, если нужно
|
||
// MessageBox.Show("Нет сохраненных магазинов...", "Внимание", MessageBoxButtons.OK, Information);
|
||
}
|
||
ResetProductData();
|
||
// Обновляем состояние кнопки GetData после загрузки
|
||
txtOfferId_TextChanged(txtOfferId, EventArgs.Empty);
|
||
}
|
||
|
||
// Попытка установить принтер по умолчанию для PrintDocument
|
||
private void TrySetDefaultPrinterForDocument()
|
||
{
|
||
string thermalPrinterName = "Xprinter XP-365B"; // Или другое имя по умолчанию
|
||
foreach (string printerName in PrinterSettings.InstalledPrinters)
|
||
{
|
||
if (printerName.Equals(thermalPrinterName, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
try
|
||
{
|
||
_printDocForSetup.PrinterSettings.PrinterName = printerName;
|
||
System.Diagnostics.Debug.WriteLine($"Принтер по умолчанию '{printerName}' установлен для PrintDocument.");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"Ошибка установки принтера по умолчанию '{printerName}': {ex.Message}");
|
||
}
|
||
return; // Выходим после нахождения
|
||
}
|
||
}
|
||
System.Diagnostics.Debug.WriteLine($"Принтер по умолчанию '{thermalPrinterName}' не найден.");
|
||
}
|
||
|
||
// --- Обработчики событий UI ---
|
||
|
||
private void btnManageStores_Click(object sender, EventArgs e)
|
||
{
|
||
using (FormManageStores manageForm = new FormManageStores())
|
||
{
|
||
manageForm.ShowDialog(this);
|
||
LoadStoresToComboBox(); // Перезагружаем список
|
||
}
|
||
}
|
||
|
||
private void cmbStores_SelectedIndexChanged(object sender, EventArgs e)
|
||
{
|
||
ResetProductData();
|
||
txtOfferId_TextChanged(txtOfferId, EventArgs.Empty); // Обновляем состояние кнопки GetData
|
||
// Можно попробовать обновить принтер по умолчанию для _printDocForSetup,
|
||
// если у разных магазинов могут быть разные принтеры (но это усложнит логику)
|
||
}
|
||
|
||
private void txtOfferId_TextChanged(object sender, EventArgs e)
|
||
{
|
||
btnGetData.Enabled = cmbStores.SelectedItem != null && !string.IsNullOrEmpty(txtOfferId.Text.Trim());
|
||
if (string.IsNullOrEmpty(txtOfferId.Text.Trim()))
|
||
{
|
||
ResetProductData();
|
||
}
|
||
}
|
||
|
||
// --- Кнопка "Настройка принтера" ---
|
||
private void btnPageSetup_Click(object sender, EventArgs e)
|
||
{
|
||
// Убедимся, что принтер в _printDocForSetup актуален (если не нашли Xprinter при запуске,
|
||
// или если у пользователя несколько принтеров)
|
||
if (!PrinterSettings.InstalledPrinters.Cast<string>().Contains(_printDocForSetup.PrinterSettings.PrinterName))
|
||
{
|
||
// Если текущий принтер недоступен, сбрасываем на принтер по умолчанию Windows
|
||
try
|
||
{
|
||
_printDocForSetup.PrinterSettings = new PrinterSettings(); // Сброс на настройки по умолчанию
|
||
System.Diagnostics.Debug.WriteLine("Текущий принтер в PrintDocument был недоступен, сброшен на принтер по умолчанию Windows.");
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
pageSetupDialog1.Document = _printDocForSetup; // Связываем диалог с нашим PrintDocument
|
||
pageSetupDialog1.MinMargins = new Margins(0, 0, 0, 0); // Минимальные поля
|
||
pageSetupDialog1.AllowMargins = true; // Разрешаем менять поля
|
||
pageSetupDialog1.AllowOrientation = false; // Запрещаем менять ориентацию
|
||
pageSetupDialog1.AllowPaper = true; // Разрешаем менять размер бумаги
|
||
pageSetupDialog1.AllowPrinter = true; // Разрешаем выбирать другой принтер
|
||
|
||
if (pageSetupDialog1.ShowDialog(this) == DialogResult.OK)
|
||
{
|
||
// Настройки применились к _printDocForSetup
|
||
lblStatus.Text = "Настройки принтера применены.";
|
||
System.Diagnostics.Debug.WriteLine($"PageSetupDialog OK: New PaperSize={_printDocForSetup.DefaultPageSettings.PaperSize.PaperName}, Printer={_printDocForSetup.PrinterSettings.PrinterName}");
|
||
}
|
||
else
|
||
{
|
||
lblStatus.Text = "Настройка принтера отменена.";
|
||
}
|
||
}
|
||
|
||
// --- Получение данных из API ---
|
||
private async void btnGetData_Click(object sender, EventArgs e)
|
||
{
|
||
OzonStore selectedStore = cmbStores.SelectedItem as OzonStore;
|
||
if (selectedStore == null) { MessageBox.Show("Выберите магазин.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; }
|
||
|
||
string clientId = selectedStore.ClientId;
|
||
string apiKey = DecryptApiKey(selectedStore.EncryptedApiKey);
|
||
if (apiKey == null) { lblStatus.Text = "Ошибка ключа API."; return; }
|
||
|
||
string rawOfferId = txtOfferId.Text.Trim();
|
||
if (string.IsNullOrEmpty(rawOfferId)) { MessageBox.Show("Введите Артикул.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; }
|
||
|
||
string offerId = Regex.Replace(rawOfferId, @"[^a-zA-Zа-яА-Я0-9_-]", "");
|
||
if (string.IsNullOrEmpty(offerId)) { MessageBox.Show("Артикул некорректен.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; }
|
||
if (offerId != rawOfferId) { txtOfferId.Text = offerId; }
|
||
|
||
ResetProductData();
|
||
lblStatus.Text = "Запрос данных...";
|
||
Application.DoEvents();
|
||
|
||
_httpClient.DefaultRequestHeaders.Remove("Client-Id");
|
||
_httpClient.DefaultRequestHeaders.Remove("Api-Key");
|
||
_httpClient.DefaultRequestHeaders.Add("Client-Id", clientId);
|
||
_httpClient.DefaultRequestHeaders.Add("Api-Key", apiKey);
|
||
|
||
string responseBody = string.Empty;
|
||
try
|
||
{
|
||
var requestData = new { offer_id = new[] { offerId } };
|
||
var jsonRequest = JsonConvert.SerializeObject(requestData);
|
||
var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
|
||
|
||
HttpResponseMessage response = await _httpClient.PostAsync("/v3/product/info/list", content);
|
||
responseBody = await response.Content.ReadAsStringAsync();
|
||
System.Diagnostics.Debug.WriteLine($"API Response ({response.StatusCode}): {responseBody}");
|
||
|
||
if (response.IsSuccessStatusCode)
|
||
{
|
||
JObject jsonResponse = JObject.Parse(responseBody);
|
||
JToken itemsArray = jsonResponse["items"];
|
||
|
||
if (itemsArray != null && itemsArray.Type == JTokenType.Array && itemsArray.HasValues)
|
||
{
|
||
JToken firstItem = itemsArray[0];
|
||
if (firstItem != null)
|
||
{
|
||
_currentProductName = firstItem["name"]?.ToString() ?? "N/A";
|
||
_currentSku = firstItem["offer_id"]?.ToString() ?? offerId;
|
||
_currentBarcode = firstItem["barcode"]?.ToString();
|
||
if (string.IsNullOrEmpty(_currentBarcode))
|
||
{
|
||
JToken barcodesToken = firstItem["barcodes"];
|
||
if (barcodesToken != null && barcodesToken.Type == JTokenType.Array && barcodesToken.HasValues)
|
||
{ _currentBarcode = barcodesToken.First?.ToString(); }
|
||
}
|
||
_currentBarcode = _currentBarcode ?? "N/A";
|
||
|
||
GenerateLabelPreview(); // Обновляем UI и включаем кнопку печати
|
||
}
|
||
else { HandleApiError("Нет данных о товаре в ответе.", responseBody); }
|
||
}
|
||
else { HandleApiError("Ответ API не содержит списка товаров.", responseBody); }
|
||
}
|
||
else { HandleApiError($"Ошибка API: {response.StatusCode}", responseBody); }
|
||
}
|
||
catch (JsonReaderException jsonEx) { MessageBox.Show($"Ошибка JSON: {jsonEx.Message}\n{responseBody}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); ResetProductData(); }
|
||
catch (HttpRequestException httpEx) { MessageBox.Show($"Ошибка сети: {httpEx.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); ResetProductData(); }
|
||
catch (Exception ex) { MessageBox.Show($"Ошибка: {ex.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); ResetProductData(); }
|
||
finally { apiKey = null; }
|
||
}
|
||
|
||
// Обработка ошибок API
|
||
private void HandleApiError(string messagePrefix, string responseBody)
|
||
{
|
||
string errorMessage = $"{messagePrefix}";
|
||
try
|
||
{
|
||
JObject errorJson = JObject.Parse(responseBody);
|
||
string ozonError = errorJson["message"]?.ToString() ?? errorJson["error"]?["message"]?.ToString();
|
||
errorMessage = string.IsNullOrEmpty(ozonError) ? $"{messagePrefix}\n{responseBody}" : $"{messagePrefix}\n{ozonError}";
|
||
}
|
||
catch { errorMessage = $"{messagePrefix}\n{responseBody}"; }
|
||
|
||
MessageBox.Show(errorMessage, "Ошибка API Ozon", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
lblStatus.Text = "Ошибка API.";
|
||
ResetProductData();
|
||
}
|
||
|
||
// Сброс данных о товаре и состояния UI
|
||
private void ResetProductData()
|
||
{
|
||
_currentProductName = null;
|
||
_currentSku = null;
|
||
_currentBarcode = null;
|
||
lblProductName.Text = "Название:";
|
||
lblProductSku.Text = "Артикул:";
|
||
lblProductBarcode.Text = "Штрихкод:";
|
||
// Очищаем превью, если оно есть
|
||
if (this.Controls.ContainsKey("picLabelPreview"))
|
||
(this.Controls["picLabelPreview"] as PictureBox).Image = null;
|
||
btnPrintLabel.Enabled = false;
|
||
// lblStatus.Text = "Статус:";
|
||
}
|
||
|
||
// Генерация (только обновление UI)
|
||
private void GenerateLabelPreview()
|
||
{
|
||
bool dataAvailable = !string.IsNullOrEmpty(_currentSku) &&
|
||
!string.IsNullOrEmpty(_currentProductName) &&
|
||
!string.IsNullOrEmpty(_currentBarcode) &&
|
||
_currentBarcode != "N/A";
|
||
if (!dataAvailable) { ResetProductData(); return; }
|
||
|
||
lblProductName.Text = $"Название: {_currentProductName}";
|
||
lblProductSku.Text = $"Артикул: {_currentSku}";
|
||
lblProductBarcode.Text = $"Штрихкод: {_currentBarcode}";
|
||
|
||
// Очистка превью, если оно есть
|
||
if (this.Controls.ContainsKey("picLabelPreview"))
|
||
(this.Controls["picLabelPreview"] as PictureBox).Image = null;
|
||
|
||
btnPrintLabel.Enabled = true;
|
||
lblStatus.Text = "Данные получены, готово к печати.";
|
||
}
|
||
|
||
|
||
// --- Печать ---
|
||
|
||
// Кнопка "Печать этикетки"
|
||
private void btnPrintLabel_Click(object sender, EventArgs e)
|
||
{
|
||
if (string.IsNullOrEmpty(_currentSku) || string.IsNullOrEmpty(_currentProductName) || string.IsNullOrEmpty(_currentBarcode) || _currentBarcode == "N/A")
|
||
{
|
||
MessageBox.Show("Нет данных для печати.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
return;
|
||
}
|
||
|
||
PrintDialog printDialog = new PrintDialog();
|
||
printDialog.Document = _printDocForSetup; // Используем настроенный документ
|
||
printDialog.AllowSomePages = false;
|
||
printDialog.AllowSelection = false;
|
||
printDialog.UseEXDialog = true;
|
||
|
||
if (printDialog.ShowDialog(this) == DialogResult.OK)
|
||
{
|
||
// _printDocForSetup уже содержит принтер и настройки страницы, выбранные в PrintDialog или PageSetupDialog
|
||
// Переустанавливаем поля на случай, если PrintDialog их сбросил
|
||
_printDocForSetup.DefaultPageSettings.Margins = new Margins(0, 0, 0, 0);
|
||
_printDocForSetup.OriginAtMargins = false;
|
||
|
||
try
|
||
{
|
||
lblStatus.Text = $"Печать на {_printDocForSetup.PrinterSettings.PrinterName}...";
|
||
_printDocForSetup.Print(); // Печатаем с текущими настройками
|
||
lblStatus.Text = "Отправлено на печать.";
|
||
}
|
||
catch (InvalidPrinterException)
|
||
{
|
||
MessageBox.Show($"Ошибка: Принтер '{_printDocForSetup.PrinterSettings.PrinterName}' недоступен или не найден.\nПроверьте подключение или выберите другой принтер в настройках.", "Ошибка принтера", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
lblStatus.Text = "Ошибка принтера.";
|
||
}
|
||
|
||
catch (Exception ex)
|
||
{
|
||
MessageBox.Show($"Ошибка печати: {ex.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
lblStatus.Text = "Ошибка печати.";
|
||
}
|
||
}
|
||
else { lblStatus.Text = "Печать отменена."; }
|
||
|
||
printDialog.Dispose();
|
||
}
|
||
|
||
// Метод отрисовки страницы для печати (динамический)
|
||
private void pd_PrintPage(object sender, PrintPageEventArgs e)
|
||
{
|
||
// Проверка данных
|
||
if (string.IsNullOrEmpty(_currentSku) || string.IsNullOrEmpty(_currentProductName) || string.IsNullOrEmpty(_currentBarcode) || _currentBarcode == "N/A")
|
||
{ System.Diagnostics.Debug.WriteLine("PrintPage ABORTED - Missing data!"); e.Cancel = true; return; }
|
||
|
||
Graphics g = e.Graphics;
|
||
// Получаем РЕАЛЬНЫЕ границы печатаемой области в единицах Graphics (обычно сотые дюйма)
|
||
// Не PageBounds, а PrintableArea! PageBounds - это физический размер листа.
|
||
RectangleF printArea = e.PageSettings.PrintableArea;
|
||
|
||
// Отступы от КРАЕВ ПЕЧАТАЕМОЙ ОБЛАСТИ (в единицах Graphics)
|
||
// Подбирайте эти значения, чтобы получить рамку внутри печатаемой зоны
|
||
float marginX = 5 * g.DpiX / 100f; // Пример: 5/100 дюйма -> в единицах Graphics
|
||
float marginY = 3 * g.DpiY / 100f; // Пример: 3/100 дюйма -> в единицах Graphics
|
||
|
||
// Область для рисования контента внутри отступов
|
||
float drawableX = printArea.Left + marginX;
|
||
float drawableY = printArea.Top + marginY;
|
||
float drawableWidth = printArea.Width - (marginX * 2);
|
||
float drawableHeight = printArea.Height - (marginY * 2);
|
||
float currentY = drawableY; // Текущая позиция по Y
|
||
|
||
// Проверка на валидность области
|
||
if (drawableWidth <= 0 || drawableHeight <= 0) { System.Diagnostics.Debug.WriteLine("PrintPage ABORTED - Invalid drawable area!"); e.Cancel = true; return; }
|
||
|
||
System.Diagnostics.Debug.WriteLine("--- Printing Page (Dynamic) ---");
|
||
System.Diagnostics.Debug.WriteLine($"Paper: {e.PageSettings.PaperSize.PaperName} ({e.PageSettings.PaperSize.Width}x{e.PageSettings.PaperSize.Height}) hund.inch");
|
||
System.Diagnostics.Debug.WriteLine($"PrintableArea ({g.PageUnit}): X={printArea.X:F2}, Y={printArea.Y:F2}, W={printArea.Width:F2}, H={printArea.Height:F2}");
|
||
System.Diagnostics.Debug.WriteLine($"Drawable Area ({g.PageUnit}): X={drawableX:F2}, Y={drawableY:F2}, W={drawableWidth:F2}, H={drawableHeight:F2}");
|
||
|
||
// Шрифты (размер в пунктах)
|
||
float baseFontSizePoints = 6f; // Базовый размер, можно менять
|
||
Font titleFont = new Font("Arial", baseFontSizePoints + 1, FontStyle.Bold);
|
||
Font regularFont = new Font("Arial", baseFontSizePoints);
|
||
Font barcodeTextFont = new Font("Arial", baseFontSizePoints - 1);
|
||
|
||
try
|
||
{
|
||
// --- Рисуем Название ---
|
||
string productName = _currentProductName ?? "N/A";
|
||
SizeF titleSize = g.MeasureString(productName, titleFont, (int)drawableWidth);
|
||
// Ограничиваем высоту, чтобы поместилось остальное
|
||
float maxTitleHeight = drawableHeight * 0.3f;
|
||
float actualTitleHeight = Math.Min(titleSize.Height, maxTitleHeight);
|
||
g.DrawString(productName, titleFont, Brushes.Black, new RectangleF(drawableX, currentY, drawableWidth, actualTitleHeight));
|
||
currentY += actualTitleHeight + (2 * g.DpiY / 72f); // Отступ 2 пункта
|
||
|
||
// --- Рисуем Артикул ---
|
||
string skuText = $"Артикул: {_currentSku ?? "N/A"}";
|
||
SizeF skuSize = g.MeasureString(skuText, regularFont, (int)drawableWidth);
|
||
if (currentY + skuSize.Height < printArea.Bottom - marginY - 15 * 100f / g.DpiY) // Оставляем ~15px под ШК + текст
|
||
{
|
||
g.DrawString(skuText, regularFont, Brushes.Black, drawableX, currentY);
|
||
currentY += skuSize.Height + (3 * g.DpiY / 72f); // Отступ 3 пункта
|
||
}
|
||
|
||
// --- Рисуем Штрихкод ---
|
||
float remainingHeight = (printArea.Bottom - marginY) - currentY; // Оставшаяся высота
|
||
if (remainingHeight > 15 * 100f / g.DpiY) // Если осталось хотя бы ~15px
|
||
{
|
||
try
|
||
{
|
||
// --- Расчет размеров ШК ---
|
||
float barcodeTextHeight = g.MeasureString(_currentBarcode, barcodeTextFont).Height;
|
||
float barcodePrintHeight = Math.Max(10 * 100f / g.DpiY, remainingHeight - barcodeTextHeight - (2 * g.DpiY / 72f)); // Вычитаем текст и отступ
|
||
float barcodePrintWidth = drawableWidth * 0.98f;
|
||
|
||
int barcodePixelHeight = (int)(barcodePrintHeight * g.DpiY / 100f);
|
||
int barcodePixelWidth = (int)(barcodePrintWidth * g.DpiX / 100f);
|
||
if (barcodePixelHeight < 10 || barcodePixelWidth < 40) throw new Exception("Слишком мало места для ШК.");
|
||
|
||
BarcodeFormat barcodeFormat = BarcodeFormat.CODE_128;
|
||
// ... (определение barcodeFormat) ...
|
||
if (_currentBarcode.Length == 13 && long.TryParse(_currentBarcode, out _)) barcodeFormat = BarcodeFormat.EAN_13;
|
||
else if (_currentBarcode.Length == 8 && long.TryParse(_currentBarcode, out _)) barcodeFormat = BarcodeFormat.EAN_8;
|
||
|
||
var barcodeWriter = new ZXing.Windows.Compatibility.BarcodeWriter { Format = barcodeFormat, Options = new EncodingOptions { Height = barcodePixelHeight, Width = barcodePixelWidth, PureBarcode = true, Margin = 1 } };
|
||
|
||
using (var barcodeBitmap = barcodeWriter.Write(_currentBarcode))
|
||
{
|
||
float barcodeX = drawableX + (drawableWidth - barcodePrintWidth) / 2.0f; // Центрируем
|
||
g.DrawImage(barcodeBitmap, barcodeX, currentY, barcodePrintWidth, barcodePrintHeight);
|
||
currentY += barcodePrintHeight + (1 * g.DpiY / 72f); // Отступ 1 пункт
|
||
}
|
||
|
||
// --- Рисуем текст ШК ---
|
||
SizeF barcodeTextSize = g.MeasureString(_currentBarcode, barcodeTextFont);
|
||
if (currentY + barcodeTextSize.Height <= printArea.Bottom - marginY)
|
||
{
|
||
float textX = drawableX + (drawableWidth - barcodeTextSize.Width) / 2.0f; // Центрируем
|
||
g.DrawString(_currentBarcode, barcodeTextFont, Brushes.Black, textX, currentY);
|
||
}
|
||
else { System.Diagnostics.Debug.WriteLine("SKIPPING Barcode text - no space"); }
|
||
|
||
}
|
||
catch (Exception exBarcode)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"Error drawing barcode: {exBarcode.Message}");
|
||
g.DrawString("Ошибка ШК", regularFont, Brushes.Red, drawableX, currentY); // Рисуем ошибку вместо ШК
|
||
}
|
||
}
|
||
else { System.Diagnostics.Debug.WriteLine("SKIPPING Barcode - not enough vertical space"); }
|
||
|
||
}
|
||
catch (Exception exDraw) { System.Diagnostics.Debug.WriteLine($"Error drawing page: {exDraw}"); }
|
||
finally { titleFont.Dispose(); regularFont.Dispose(); barcodeTextFont.Dispose(); }
|
||
|
||
e.HasMorePages = false;
|
||
}
|
||
|
||
} // Конец класса Form1
|
||
} // Конец namespace |