// 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; }