Files
AveryDennisonUtil/LabelLayout.cpp
T
yoshikazu 19d1c39370 Initial commit: OtozaiPrinter (AveryDennisonUtil) ラベル/レシート レイアウト設計・印刷 MFC アプリ
- 用紙設計キャンバス(1/100mm 基準)・テキスト/画像/矩形/直線アイテム
- コピー/複製/Zオーダー/整列/グリッドスナップ、原紙(下絵)読込と印刷可否
- GDI 直接印刷、.otz 保存/再読込(版数つき)
- MFC/CRT 静的リンクで VS2022 IDE から F5 デバッグ可
2026-06-05 17:27:29 +09:00

279 lines
8.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// LabelLayout.cpp
#include "pch.h"
#include "LabelLayout.h"
#include "ImageItem.h"
// コンストラクタ。既定の用紙(40×30mm, 203dpi, 余白0, 原紙なし)で初期化。
CLabelLayout::CLabelLayout()
: m_paperWidthHu(4000) // 既定 40mm × 30mm (ロールラベル)
, m_paperHeightHu(3000)
, m_dpi(203)
, m_marginHu(0)
, m_pTemplate(nullptr)
, m_printTemplate(FALSE)
{
}
// デストラクタ。全アイテムと原紙を解放する。
CLabelLayout::~CLabelLayout()
{
RemoveAll();
delete m_pTemplate;
}
// 内容を空の既定レイアウトへ戻す(「新規」用)。
void CLabelLayout::Reset()
{
RemoveAll();
ClearTemplate();
m_paperWidthHu = 4000;
m_paperHeightHu = 3000;
m_dpi = 203;
m_marginHu = 0;
m_printTemplate = FALSE;
}
// 原紙(背景下絵)を読み込む。用紙全体に敷くため fit=0(伸縮)で取り込む。
// 失敗時は既存の原紙を保ったまま FALSE を返す。
BOOL CLabelLayout::LoadTemplate(LPCTSTR path)
{
CImageItem* t = new CImageItem();
t->m_fit = 0; // 用紙矩形いっぱいに伸ばす(位置が一意に決まる)
if (!t->LoadFromFile(path))
{
delete t;
return FALSE;
}
delete m_pTemplate;
m_pTemplate = t;
SyncTemplateRect();
return TRUE;
}
// 原紙を破棄する(「消去」用)。
void CLabelLayout::ClearTemplate()
{
delete m_pTemplate;
m_pTemplate = nullptr;
}
// 原紙の矩形を現在の用紙サイズに合わせる(用紙サイズ変更時などに呼ぶ)。
void CLabelLayout::SyncTemplateRect()
{
if (m_pTemplate)
m_pTemplate->SetRect(CRect(0, 0, m_paperWidthHu, m_paperHeightHu));
}
// 原紙画像の画素サイズ(px)を取得。原紙が無ければ FALSE。
BOOL CLabelLayout::GetTemplatePixelSize(int& w, int& h) const
{
return m_pTemplate ? m_pTemplate->GetPixelSize(w, h) : FALSE;
}
// 原紙画像の埋め込み解像度(DPI)を取得。情報が無ければ FALSE。
BOOL CLabelLayout::GetTemplateDpi(double& dx, double& dy) const
{
return m_pTemplate ? m_pTemplate->GetResolutionDpi(dx, dy) : FALSE;
}
// 原紙の画素数から用紙サイズ(1/100mm)を決め直す。
// useEmbeddedDpi=TRUE なら画像の埋め込みDPI、FALSE(または取得不可)なら
// m_dpi を基準に px→mm 換算する。
void CLabelLayout::SetPaperSizeFromTemplate(BOOL useEmbeddedDpi)
{
if (!m_pTemplate) return;
int w = 0, h = 0;
if (!m_pTemplate->GetPixelSize(w, h) || w <= 0 || h <= 0) return;
double dx = m_dpi, dy = m_dpi;
if (useEmbeddedDpi)
{
double ex = 0, ey = 0;
if (m_pTemplate->GetResolutionDpi(ex, ey) && ex > 0 && ey > 0) { dx = ex; dy = ey; }
}
if (dx <= 0) dx = 203;
if (dy <= 0) dy = 203;
int nw = (int)(w * 2540.0 / dx + 0.5); // px / dpi * 2540 = 1/100mm
int nh = (int)(h * 2540.0 / dy + 0.5);
if (nw > 0) m_paperWidthHu = nw;
if (nh > 0) m_paperHeightHu = nh;
SyncTemplateRect();
}
// 全アイテムを破棄して配列を空にする(原紙は対象外)。
void CLabelLayout::RemoveAll()
{
for (INT_PTR i = 0; i < m_items.GetCount(); ++i)
delete (CPrintItem*)m_items.GetAt(i);
m_items.RemoveAll();
}
// アイテムを最前面(配列末尾)に追加する。所有権はレイアウトが持つ。
void CLabelLayout::AddItem(CPrintItem* p)
{
m_items.Add(p);
}
// 指定アイテムを配列から外して破棄する。
void CLabelLayout::DeleteItem(CPrintItem* p)
{
for (INT_PTR i = 0; i < m_items.GetCount(); ++i)
{
if ((CPrintItem*)m_items.GetAt(i) == p)
{
m_items.RemoveAt(i);
delete p;
return;
}
}
}
// 指定座標(1/100mm)に当たるアイテムを最前面優先で返す。無ければ nullptr。
CPrintItem* CLabelLayout::HitTest(const CPoint& huPt) const
{
for (INT_PTR i = m_items.GetCount() - 1; i >= 0; --i)
{
CPrintItem* p = (CPrintItem*)m_items.GetAt(i);
if (p->HitTest(huPt))
return p;
}
return nullptr;
}
CRect CLabelLayout::ContentRect() const
{
if (m_marginHu <= 0)
return CRect(0, 0, m_paperWidthHu, m_paperHeightHu);
int m = m_marginHu;
// 余白が大きすぎても潰れないようにクランプ
if (m * 2 >= m_paperWidthHu) m = (m_paperWidthHu - 1) / 2;
if (m * 2 >= m_paperHeightHu) m = (m_paperHeightHu - 1) / 2;
if (m < 0) m = 0;
return CRect(m, m, m_paperWidthHu - m, m_paperHeightHu - m);
}
// 配列内でアイテム p の添字を返す(見つからなければ -1)。z オーダー操作の補助。
static INT_PTR FindIndex(const CObArray& items, CPrintItem* p)
{
for (INT_PTR i = 0; i < items.GetCount(); ++i)
if ((CPrintItem*)items.GetAt(i) == p) return i;
return -1;
}
// 最前面へ(配列末尾へ移動)。
void CLabelLayout::BringToFront(CPrintItem* p)
{
INT_PTR i = FindIndex(m_items, p);
if (i < 0 || i == m_items.GetCount() - 1) return;
m_items.RemoveAt(i);
m_items.Add(p);
}
// 最背面へ(配列先頭へ移動)。
void CLabelLayout::SendToBack(CPrintItem* p)
{
INT_PTR i = FindIndex(m_items, p);
if (i <= 0) return;
m_items.RemoveAt(i);
m_items.InsertAt(0, p);
}
// 1つ前面へ(隣と入れ替え)。
void CLabelLayout::BringForward(CPrintItem* p)
{
INT_PTR i = FindIndex(m_items, p);
if (i < 0 || i == m_items.GetCount() - 1) return;
m_items.RemoveAt(i);
m_items.InsertAt(i + 1, p);
}
// 1つ背面へ(隣と入れ替え)。
void CLabelLayout::SendBackward(CPrintItem* p)
{
INT_PTR i = FindIndex(m_items, p);
if (i <= 0) return;
m_items.RemoveAt(i);
m_items.InsertAt(i - 1, p);
}
// .otz 形式マーカ。先頭 int がこれなら新形式(版数つき)、違えば旧形式
// (先頭 int を m_paperWidthHu として読む)として後方互換を保つ。
static const int kOtzMagic = 0x4F545A31; // 'OTZ1'
static const int kOtzVersion = 3; // 2:m_marginHu / 3:原紙(template)
// レイアウト全体(用紙設定・余白・原紙・全アイテム)を .otz へ読み書きする。
// 先頭にマジック+版数を持つ新形式。マジックが無いファイルは旧形式として読む。
void CLabelLayout::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
ar << kOtzMagic << kOtzVersion;
ar << m_paperWidthHu << m_paperHeightHu << m_dpi << m_marginHu;
ar << (int)m_printTemplate;
ar << (CObject*)m_pTemplate; // nullptr も CArchive が扱う
}
else
{
// 既存の原紙は破棄してから読み直す
delete m_pTemplate;
m_pTemplate = nullptr;
m_printTemplate = FALSE;
int first;
ar >> first;
if (first == kOtzMagic)
{
int ver;
ar >> ver;
ar >> m_paperWidthHu >> m_paperHeightHu >> m_dpi;
m_marginHu = 0;
if (ver >= 2) ar >> m_marginHu;
if (ver >= 3)
{
int pt; ar >> pt; m_printTemplate = pt;
CObject* pObj = nullptr;
ar >> pObj;
if (pObj && pObj->IsKindOf(RUNTIME_CLASS(CImageItem)))
m_pTemplate = (CImageItem*)pObj;
else
delete pObj; // 想定外の型(通常 nullptr)
}
}
else
{
// 旧形式: 先頭 int は m_paperWidthHu
m_paperWidthHu = first;
ar >> m_paperHeightHu >> m_dpi;
m_marginHu = 0;
}
SyncTemplateRect();
}
// CObArray::Serialize が各 CPrintItem* を多態的に保存/復元する
m_items.Serialize(ar);
}
// レイアウトを指定パスの .otz ファイルへ保存する。成功で TRUE。
BOOL CLabelLayout::SaveToFile(LPCTSTR path)
{
CFile file;
if (!file.Open(path, CFile::modeCreate | CFile::modeWrite))
return FALSE;
CArchive ar(&file, CArchive::store);
try { Serialize(ar); ar.Close(); }
catch (CException* e) { e->Delete(); return FALSE; }
return TRUE;
}
// 指定パスの .otz ファイルからレイアウトを読み込む。成功で TRUE。
BOOL CLabelLayout::LoadFromFile(LPCTSTR path)
{
CFile file;
if (!file.Open(path, CFile::modeRead | CFile::shareDenyWrite))
return FALSE;
CArchive ar(&file, CArchive::load);
try { RemoveAll(); Serialize(ar); ar.Close(); }
catch (CException* e) { e->Delete(); return FALSE; }
return TRUE;
}