Initial commit: OtozaiPrinter (AveryDennisonUtil) ラベル/レシート レイアウト設計・印刷 MFC アプリ
- 用紙設計キャンバス(1/100mm 基準)・テキスト/画像/矩形/直線アイテム - コピー/複製/Zオーダー/整列/グリッドスナップ、原紙(下絵)読込と印刷可否 - GDI 直接印刷、.otz 保存/再読込(版数つき) - MFC/CRT 静的リンクで VS2022 IDE から F5 デバッグ可
This commit is contained in:
+278
@@ -0,0 +1,278 @@
|
||||
// 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;
|
||||
}
|
||||
Reference in New Issue
Block a user