Initial commit: OtozaiPrinter (AveryDennisonUtil) ラベル/レシート レイアウト設計・印刷 MFC アプリ

- 用紙設計キャンバス(1/100mm 基準)・テキスト/画像/矩形/直線アイテム
- コピー/複製/Zオーダー/整列/グリッドスナップ、原紙(下絵)読込と印刷可否
- GDI 直接印刷、.otz 保存/再読込(版数つき)
- MFC/CRT 静的リンクで VS2022 IDE から F5 デバッグ可
This commit is contained in:
yoshikazu
2026-06-05 17:27:29 +09:00
commit 19d1c39370
33 changed files with 3049 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
# エディタ/ツールのローカル設定
.claude/
# ビルド成果物
/bin/
/obj/
*.user
*.aps
*.suo
.vs/
ipch/
*.pdb
*.ilk
*.idb
+31
View File
@@ -0,0 +1,31 @@
// AveryDennisonUtil.cpp : アプリケーション クラスの実装(ダイアログベース)
#include "pch.h"
#include "AveryDennisonUtil.h"
#include "MainDialog.h"
// アプリケーションの唯一のインスタンス(MFC の作法に従いグローバルに1つ置く)
CAveryDennisonUtilApp theApp;
// コンストラクタ。特別な初期化は InitInstance で行うため、ここでは何もしない。
CAveryDennisonUtilApp::CAveryDennisonUtilApp()
{
}
// アプリ起動時に1度だけ呼ばれる初期化処理。
// 本アプリはダイアログベースなので、メインダイアログをモーダル表示し、
// それが閉じられたらメッセージループへ入らずそのまま終了する。
BOOL CAveryDennisonUtilApp::InitInstance()
{
CWinApp::InitInstance();
// 設定の保存先(レジストリ)キー。将来の設定永続化に備えて指定しておく。
SetRegistryKey(_T("Otozai"));
// メイン ダイアログを表示(ダイアログベース アプリ)
CMainDialog dlg;
m_pMainWnd = &dlg;
dlg.DoModal();
// ダイアログが閉じられたらメッセージ ポンプを開始せずに終了する
return FALSE;
}
+19
View File
@@ -0,0 +1,19 @@
// AveryDennisonUtil.h : アプリケーション クラス(ダイアログベース)
#pragma once
#ifndef __AFXWIN_H__
#error "include 'pch.h' before including this file for PCH"
#endif
#include "resource.h"
// アプリケーション本体。CWinApp を継承し、起動・終了を司る。
// ダイアログベースのため UI 本体は CMainDialog 側にある。
class CAveryDennisonUtilApp : public CWinApp
{
public:
CAveryDennisonUtilApp();
BOOL InitInstance() override; // 起動時の初期化(メインダイアログ表示)
};
extern CAveryDennisonUtilApp theApp;
+148
View File
@@ -0,0 +1,148 @@
// AveryDennisonUtil.rc : リソース スクリプト(ダイアログベース)
#include "resource.h"
#include "afxres.h"
#include "verrsrc.h"
#pragma code_page(65001)
#ifdef _WIN32
LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
#endif
/////////////////////////////////////////////////////////////////////////////
// メイン ダイアログ
//
IDD_MAIN_DIALOG DIALOGEX 0, 0, 474, 424
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "AveryDennisonUtil"
FONT 9, "MS UI Gothic", 0, 0, 0x1
BEGIN
PUSHBUTTON "新規(&N)", ID_FILE_NEW, 7, 7, 64, 14
PUSHBUTTON "開く(&O)...", ID_FILE_OPEN, 7, 24, 64, 14
PUSHBUTTON "保存(&S)", ID_FILE_SAVE, 7, 41, 64, 14
PUSHBUTTON "名前を付けて保存",ID_FILE_SAVE_AS, 7, 58, 64, 14
PUSHBUTTON "テキスト(&T)...", ID_INSERT_TEXT, 7, 82, 64, 14
PUSHBUTTON "画像(&G)...", ID_INSERT_IMAGE, 7, 99, 64, 14
PUSHBUTTON "矩形枠(&R)", ID_INSERT_RECT, 7, 116, 64, 14
PUSHBUTTON "直線(&L)", ID_INSERT_LINE, 7, 133, 64, 14
PUSHBUTTON "プロパティ(&P)...",ID_EDIT_PROPERTIES,7, 157, 64, 14
PUSHBUTTON "削除(&D)", ID_EDIT_DELETE_ITEM,7,174, 64, 14
PUSHBUTTON "コピー(&C)", ID_ITEM_COPY, 7, 191, 31, 14
PUSHBUTTON "貼付(&V)", ID_ITEM_PASTE, 40, 191, 31, 14
PUSHBUTTON "複製", ID_ITEM_DUPLICATE, 7, 208, 64, 14
PUSHBUTTON "前面", ID_ITEM_FRONT, 7, 225, 31, 14
PUSHBUTTON "背面", ID_ITEM_BACK, 40, 225, 31, 14
PUSHBUTTON "整列...", ID_ALIGN_MENU, 7, 242, 64, 14
CONTROL "グリッド/スナップ",IDC_CHECK_GRID,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,261,64,12
PUSHBUTTON "原紙(&K)...", ID_TEMPLATE_LOAD, 7, 278, 31, 14
PUSHBUTTON "消去", ID_TEMPLATE_CLEAR, 40, 278, 31, 14
CONTROL "原紙を印刷", IDC_CHECK_PRINT_TEMPLATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,295,64,12
PUSHBUTTON "用紙サイズ...", ID_PAPER_SIZE, 7, 314, 64, 14
PUSHBUTTON "拡大(&I)", ID_VIEW_ZOOMIN, 7, 331, 31, 14
PUSHBUTTON "縮小(&U)", ID_VIEW_ZOOMOUT, 40, 331, 31, 14
PUSHBUTTON "印刷(&R)...", ID_FILE_PRINT, 7, 355, 64, 14
PUSHBUTTON "バージョン情報...",ID_APP_ABOUT, 7, 379, 64, 14
PUSHBUTTON "終了(&X)", IDCANCEL, 7, 403, 64, 14
CONTROL "",IDC_CANVAS,"Static",SS_BLACKFRAME,78,7,389,410
END
/////////////////////////////////////////////////////////////////////////////
// バージョン情報ダイアログ
//
IDD_ABOUTBOX DIALOGEX 0, 0, 250, 80
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "バージョン情報"
FONT 9, "MS UI Gothic", 0, 0, 0x1
BEGIN
LTEXT "AveryDennisonUtil — ラベル/レシート レイアウト設計ツール",IDC_STATIC,10,12,230,12
LTEXT "Copyright (C) 2026",IDC_STATIC,10,30,230,10
DEFPUSHBUTTON "OK",IDOK,100,56,50,16,WS_GROUP
END
IDD_PAPER_SIZE DIALOGEX 0, 0, 230, 152
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "用紙サイズ (全印刷領域)"
FONT 9, "MS UI Gothic", 0, 0, 0x1
BEGIN
LTEXT "プリセット:",IDC_STATIC,12,14,50,10
COMBOBOX IDC_COMBO_PRESET,70,12,148,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
LTEXT "幅 (mm):",IDC_STATIC,12,40,50,10
EDITTEXT IDC_EDIT_WIDTH,70,38,60,14,ES_AUTOHSCROLL
LTEXT "高さ (mm):",IDC_STATIC,12,60,50,10
EDITTEXT IDC_EDIT_HEIGHT,70,58,60,14,ES_AUTOHSCROLL
LTEXT "解像度 (dpi):",IDC_STATIC,12,80,50,10
EDITTEXT IDC_EDIT_DPI,70,78,60,14,ES_AUTOHSCROLL | ES_NUMBER
LTEXT "余白 (mm):",IDC_STATIC,12,100,50,10
EDITTEXT IDC_EDIT_MARGIN,70,98,60,14,ES_AUTOHSCROLL
DEFPUSHBUTTON "OK",IDOK,108,126,50,16,WS_GROUP
PUSHBUTTON "キャンセル",IDCANCEL,166,126,50,16
END
IDD_TEXT_PROPS DIALOGEX 0, 0, 260, 200
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "テキストのプロパティ"
FONT 9, "MS UI Gothic", 0, 0, 0x1
BEGIN
LTEXT "テキスト:",IDC_STATIC,12,12,50,10
EDITTEXT IDC_EDIT_TEXT,12,26,236,70,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL
LTEXT "フォント:",IDC_STATIC,12,104,50,10
EDITTEXT IDC_EDIT_FONT,70,102,120,14,ES_AUTOHSCROLL
LTEXT "サイズ (pt):",IDC_STATIC,12,124,50,10
EDITTEXT IDC_EDIT_PTSIZE,70,122,50,14,ES_AUTOHSCROLL
LTEXT "配置:",IDC_STATIC,12,144,50,10
COMBOBOX IDC_COMBO_ALIGN,70,142,80,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
CONTROL "太字",IDC_CHECK_BOLD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,160,144,60,12
DEFPUSHBUTTON "OK",IDOK,138,172,50,16,WS_GROUP
PUSHBUTTON "キャンセル",IDCANCEL,196,172,50,16
END
/////////////////////////////////////////////////////////////////////////////
// バージョン情報
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "041104b0"
BEGIN
VALUE "CompanyName", "Otozai"
VALUE "FileDescription", "AveryDennisonUtil"
VALUE "FileVersion", "1.0.0.0"
VALUE "InternalName", "AveryDennisonUtil.exe"
VALUE "OriginalFilename", "AveryDennisonUtil.exe"
VALUE "ProductName", "AveryDennisonUtil"
VALUE "ProductVersion", "1.0.0.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x411, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
// 文字列テーブル
//
STRINGTABLE
BEGIN
AFX_IDS_APP_TITLE "AveryDennisonUtil"
AFX_IDS_IDLEMESSAGE "準備完了"
END
/////////////////////////////////////////////////////////////////////////////
// MFC 標準リソース(共通ダイアログの文字列等)
//
#ifndef APSTUDIO_INVOKED
#include "afxres.rc"
#endif
+22
View File
@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35222.181
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AveryDennisonUtil", "AveryDennisonUtil.vcxproj", "{A1B2C3D4-E5F6-47A8-9B0C-1D2E3F405162}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A1B2C3D4-E5F6-47A8-9B0C-1D2E3F405162}.Debug|x64.ActiveCfg = Debug|x64
{A1B2C3D4-E5F6-47A8-9B0C-1D2E3F405162}.Debug|x64.Build.0 = Debug|x64
{A1B2C3D4-E5F6-47A8-9B0C-1D2E3F405162}.Release|x64.ActiveCfg = Release|x64
{A1B2C3D4-E5F6-47A8-9B0C-1D2E3F405162}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
+175
View File
@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- 構成リスト (x64 のみ) -->
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<!-- グローバル設定 -->
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<ProjectGuid>{A1B2C3D4-E5F6-47A8-9B0C-1D2E3F405162}</ProjectGuid>
<RootNamespace>AveryDennisonUtil</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<Keyword>MFCProj</Keyword>
<!-- このマシンには vcpkg のグローバル統合(integrate install)が入っており、
全 C++ プロジェクトへ applocal.ps1 デプロイ手順と include パスが注入される。
本プロジェクトは vcpkg 依存が無いので統合を無効化する。
(これで「pwsh.exe が見つからない」というビルド時の警告も消える) -->
<VcpkgEnabled>false</VcpkgEnabled>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<!-- 構成プロパティ -->
<!-- MFC・CRT とも「静的リンク」(UseOfMfc=Static / RuntimeLibrary=非DLL)。
理由: 単一 exe で外部 DLL に依存しないため、VS2022 IDE の F5 デバッグでも
単体起動でも追加の DLL 配置なしにそのまま動く(Debug 用 MFC DLL の配置不要)。
GDI+(gdiplus.dll)・msimg32.dll は OS 標準 DLL なので配置不要。 -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<UseOfMfc>Static</UseOfMfc>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<UseOfMfc>Static</UseOfMfc>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="Shared" />
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props"
Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')"
Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props"
Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')"
Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<!-- 出力・中間ディレクトリ (隣 STEPViewerVC に準拠) -->
<PropertyGroup>
<OutDir>$(SolutionDir)bin\$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)obj\AveryDennisonUtil\$(Configuration)\</IntDir>
<TargetName>AveryDennisonUtil</TargetName>
</PropertyGroup>
<!-- Debug ビルド設定 -->
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PreprocessorDefinitions>WIN32;_WINDOWS;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<!-- Release ビルド設定 -->
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PreprocessorDefinitions>WIN32;_WINDOWS;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<Culture>0x0411</Culture>
</ResourceCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<!-- ソースファイル -->
<ItemGroup>
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="AveryDennisonUtil.cpp" />
<ClCompile Include="MainDialog.cpp" />
<ClCompile Include="Canvas.cpp" />
<ClCompile Include="LabelLayout.cpp" />
<ClCompile Include="RenderScene.cpp" />
<ClCompile Include="PrintItem.cpp" />
<ClCompile Include="TextItem.cpp" />
<ClCompile Include="ShapeItem.cpp" />
<ClCompile Include="ImageItem.cpp" />
<ClCompile Include="TextPropsDlg.cpp" />
<ClCompile Include="PaperSizeDlg.cpp" />
</ItemGroup>
<!-- ヘッダファイル -->
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="CoordMap.h" />
<ClInclude Include="AveryDennisonUtil.h" />
<ClInclude Include="MainDialog.h" />
<ClInclude Include="Canvas.h" />
<ClInclude Include="LabelLayout.h" />
<ClInclude Include="RenderScene.h" />
<ClInclude Include="PrintItem.h" />
<ClInclude Include="TextItem.h" />
<ClInclude Include="ShapeItem.h" />
<ClInclude Include="ImageItem.h" />
<ClInclude Include="TextPropsDlg.h" />
<ClInclude Include="PaperSizeDlg.h" />
</ItemGroup>
<!-- リソース -->
<ItemGroup>
<ResourceCompile Include="AveryDennisonUtil.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>
+57
View File
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="ソース ファイル">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;cxx;cc</Extensions>
</Filter>
<Filter Include="ヘッダー ファイル">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp</Extensions>
</Filter>
<Filter Include="リソース ファイル">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;bmp</Extensions>
</Filter>
<Filter Include="ソース ファイル\items">
<UniqueIdentifier>{1a2b3c4d-0001-0002-0003-000405060708}</UniqueIdentifier>
</Filter>
<Filter Include="ヘッダー ファイル\items">
<UniqueIdentifier>{1a2b3c4d-0011-0012-0013-001405060708}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp"><Filter>ソース ファイル</Filter></ClCompile>
<ClCompile Include="AveryDennisonUtil.cpp"><Filter>ソース ファイル</Filter></ClCompile>
<ClCompile Include="MainDialog.cpp"><Filter>ソース ファイル</Filter></ClCompile>
<ClCompile Include="Canvas.cpp"><Filter>ソース ファイル</Filter></ClCompile>
<ClCompile Include="LabelLayout.cpp"><Filter>ソース ファイル</Filter></ClCompile>
<ClCompile Include="RenderScene.cpp"><Filter>ソース ファイル</Filter></ClCompile>
<ClCompile Include="TextPropsDlg.cpp"><Filter>ソース ファイル</Filter></ClCompile>
<ClCompile Include="PaperSizeDlg.cpp"><Filter>ソース ファイル</Filter></ClCompile>
<ClCompile Include="PrintItem.cpp"><Filter>ソース ファイル\items</Filter></ClCompile>
<ClCompile Include="TextItem.cpp"><Filter>ソース ファイル\items</Filter></ClCompile>
<ClCompile Include="ShapeItem.cpp"><Filter>ソース ファイル\items</Filter></ClCompile>
<ClCompile Include="ImageItem.cpp"><Filter>ソース ファイル\items</Filter></ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h"><Filter>ヘッダー ファイル</Filter></ClInclude>
<ClInclude Include="targetver.h"><Filter>ヘッダー ファイル</Filter></ClInclude>
<ClInclude Include="resource.h"><Filter>ヘッダー ファイル</Filter></ClInclude>
<ClInclude Include="CoordMap.h"><Filter>ヘッダー ファイル</Filter></ClInclude>
<ClInclude Include="AveryDennisonUtil.h"><Filter>ヘッダー ファイル</Filter></ClInclude>
<ClInclude Include="MainDialog.h"><Filter>ヘッダー ファイル</Filter></ClInclude>
<ClInclude Include="Canvas.h"><Filter>ヘッダー ファイル</Filter></ClInclude>
<ClInclude Include="LabelLayout.h"><Filter>ヘッダー ファイル</Filter></ClInclude>
<ClInclude Include="RenderScene.h"><Filter>ヘッダー ファイル</Filter></ClInclude>
<ClInclude Include="TextPropsDlg.h"><Filter>ヘッダー ファイル</Filter></ClInclude>
<ClInclude Include="PaperSizeDlg.h"><Filter>ヘッダー ファイル</Filter></ClInclude>
<ClInclude Include="PrintItem.h"><Filter>ヘッダー ファイル\items</Filter></ClInclude>
<ClInclude Include="TextItem.h"><Filter>ヘッダー ファイル\items</Filter></ClInclude>
<ClInclude Include="ShapeItem.h"><Filter>ヘッダー ファイル\items</Filter></ClInclude>
<ClInclude Include="ImageItem.h"><Filter>ヘッダー ファイル\items</Filter></ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="AveryDennisonUtil.rc"><Filter>リソース ファイル</Filter></ResourceCompile>
</ItemGroup>
</Project>
+583
View File
@@ -0,0 +1,583 @@
// Canvas.cpp
#include "pch.h"
#include "Canvas.h"
#include "LabelLayout.h"
#include "RenderScene.h"
#include "TextItem.h"
#include "ShapeItem.h"
#include "ImageItem.h"
#include "TextPropsDlg.h"
BEGIN_MESSAGE_MAP(CCanvas, CWnd)
ON_WM_PAINT()
ON_WM_ERASEBKGND()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDBLCLK()
ON_WM_SETCURSOR()
ON_WM_KEYDOWN()
ON_WM_GETDLGCODE()
END_MESSAGE_MAP()
// コンストラクタ。ズーム3倍・スナップ有効・グリッド1mm を既定値に初期化。
CCanvas::CCanvas()
: m_zoom(3.0)
, m_pLayout(nullptr)
, m_pSelected(nullptr)
, m_pClipboard(nullptr)
, m_dirty(FALSE)
, m_snap(TRUE)
, m_gridHu(100) // 1mm
, m_drag(Drag::None)
, m_dragHandle(-1)
, m_dragStartHu(0, 0)
, m_origRect(0, 0, 0, 0)
{
}
// デストラクタ。コピー用クリップボードのアイテムを解放する。
CCanvas::~CCanvas()
{
delete m_pClipboard;
}
// 値(1/100mm)を最も近いグリッド線へ丸める。スナップ移動・リサイズで使用。
int CCanvas::SnapHu(int hu) const
{
if (m_gridHu <= 0) return hu;
int half = m_gridHu / 2;
return ((hu + (hu >= 0 ? half : -half)) / m_gridHu) * m_gridHu;
}
// 編集キャンバス(子ウィンドウ)を生成する。ダブルクリック受信・背景灰色。
BOOL CCanvas::Create(CWnd* pParent, const CRect& rc, UINT id)
{
LPCTSTR cls = AfxRegisterWndClass(
CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
::LoadCursor(nullptr, IDC_ARROW),
(HBRUSH)::GetStockObject(LTGRAY_BRUSH));
return CWnd::Create(cls, _T(""),
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP,
rc, pParent, id);
}
// 指定 DPI と現在のズーム・余白から座標変換器(CoordMap)を作る。
CoordMap CCanvas::MakeMap(int dpiX, int dpiY) const
{
return CoordMap(dpiX, dpiY, m_zoom, kMargin, kMargin);
}
// 画面 DC の実 DPI を使って座標変換器を作る(上のオーバーロードの簡易版)。
CoordMap CCanvas::MakeMap() const
{
CClientDC dc(const_cast<CCanvas*>(this));
return MakeMap(dc.GetDeviceCaps(LOGPIXELSX), dc.GetDeviceCaps(LOGPIXELSY));
}
// クライアント座標(ピクセル)を内容座標(1/100mm)へ変換する。
CPoint CCanvas::HuFromClient(CPoint pt)
{
return MakeMap().DevToMm(pt); // スクロール無し: クライアント=内容座標
}
// ---- 描画 ----------------------------------------------------------------
BOOL CCanvas::OnEraseBkgnd(CDC* /*pDC*/)
{
return TRUE; // OnPaint で全面描画(ちらつき防止)
}
// 再描画。ちらつき防止のためメモリ DC(ダブルバッファ)に一旦描いてから転送する。
// 用紙外を灰色で塗り、Render::DrawLayout で用紙・原紙・グリッド・アイテムを描く。
void CCanvas::OnPaint()
{
CPaintDC dc(this);
CRect client;
GetClientRect(client);
// ダブルバッファ
CDC mem;
mem.CreateCompatibleDC(&dc);
CBitmap bmp;
bmp.CreateCompatibleBitmap(&dc, client.Width(), client.Height());
CBitmap* pOldBmp = mem.SelectObject(&bmp);
// 用紙外(灰色)
mem.FillSolidRect(client, RGB(200, 200, 200));
if (m_pLayout)
{
CoordMap map = MakeMap(mem.GetDeviceCaps(LOGPIXELSX), mem.GetDeviceCaps(LOGPIXELSY));
Render::DrawLayout(mem, *m_pLayout, map, /*screen=*/true, m_pSelected,
m_snap ? m_gridHu : 0);
}
dc.BitBlt(0, 0, client.Width(), client.Height(), &mem, 0, 0, SRCCOPY);
mem.SelectObject(pOldBmp);
}
// ---- ズーム --------------------------------------------------------------
// 表示倍率を設定する(0.25〜30倍にクランプ)。再描画も行う。
void CCanvas::SetZoom(double z)
{
if (z < 0.25) z = 0.25;
if (z > 30.0) z = 30.0;
m_zoom = z;
Invalidate();
}
// 用紙全体がキャンバスに収まる倍率へ自動調整する。
void CCanvas::FitZoom()
{
if (!m_pLayout) return;
CRect client;
GetClientRect(client);
CClientDC dc(this);
int dpiX = dc.GetDeviceCaps(LOGPIXELSX);
int dpiY = dc.GetDeviceCaps(LOGPIXELSY);
// zoom=1 のときの用紙デバイス幅/高さ
double pw = (double)m_pLayout->m_paperWidthHu * dpiX / 2540.0;
double ph = (double)m_pLayout->m_paperHeightHu * dpiY / 2540.0;
double zx = (client.Width() - kMargin * 2) / max(1.0, pw);
double zy = (client.Height() - kMargin * 2) / max(1.0, ph);
SetZoom(min(zx, zy));
}
// ---- マウス --------------------------------------------------------------
// 左ボタン押下。選択アイテムのハンドル上ならリサイズ開始、
// アイテム本体上なら選択+移動開始、何も無ければ選択解除。
void CCanvas::OnLButtonDown(UINT nFlags, CPoint point)
{
SetFocus();
if (!m_pLayout) return;
CPoint huPt = HuFromClient(point);
if (m_pSelected)
{
CoordMap map = MakeMap();
CRect devRect = map.MmToDev(m_pSelected->Rect());
int handle = CPrintItem::HitHandle(devRect, point);
if (handle >= 0)
{
m_drag = Drag::Resize;
m_dragHandle = handle;
m_dragStartHu = huPt;
m_origRect = m_pSelected->Rect();
SetCapture();
return;
}
}
CPrintItem* hit = m_pLayout->HitTest(huPt);
if (hit != m_pSelected)
{
m_pSelected = hit;
Invalidate();
}
if (hit)
{
m_drag = Drag::Move;
m_dragStartHu = huPt;
m_origRect = hit->Rect();
SetCapture();
}
else
{
m_drag = Drag::None;
}
CWnd::OnLButtonDown(nFlags, point);
}
// マウス移動。ドラッグ中なら選択アイテムを移動/リサイズする。
// スナップ有効時は結果の座標をグリッドへ吸着。最小サイズも保証。
void CCanvas::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_drag != Drag::None && m_pSelected)
{
CPoint huPt = HuFromClient(point);
int dx = huPt.x - m_dragStartHu.x;
int dy = huPt.y - m_dragStartHu.y;
CRect r = m_origRect;
if (m_drag == Drag::Move)
{
r.OffsetRect(dx, dy);
if (m_snap)
{
// 左上をグリッドへスナップ(サイズは保つ)
r.OffsetRect(SnapHu(r.left) - r.left, SnapHu(r.top) - r.top);
}
}
else
{
switch (m_dragHandle)
{
case 0: r.left += dx; r.top += dy; break;
case 1: r.top += dy; break;
case 2: r.right += dx; r.top += dy; break;
case 3: r.right += dx; break;
case 4: r.right += dx; r.bottom += dy; break;
case 5: r.bottom += dy; break;
case 6: r.left += dx; r.bottom += dy; break;
case 7: r.left += dx; break;
}
if (m_snap)
{
r.left = SnapHu(r.left); r.top = SnapHu(r.top);
r.right = SnapHu(r.right); r.bottom = SnapHu(r.bottom);
}
if (r.Width() < 50) r.right = r.left + 50;
if (r.Height() < 50) r.bottom = r.top + 50;
}
m_pSelected->SetRect(r);
m_dirty = TRUE;
Invalidate();
}
CWnd::OnMouseMove(nFlags, point);
}
// 左ボタン離し。ドラッグ(移動/リサイズ)を終了しキャプチャを解放する。
void CCanvas::OnLButtonUp(UINT nFlags, CPoint point)
{
if (m_drag != Drag::None)
{
m_drag = Drag::None;
m_dragHandle = -1;
if (GetCapture() == this)
ReleaseCapture();
}
CWnd::OnLButtonUp(nFlags, point);
}
// ダブルクリック。アイテム上ならそれを選択してプロパティ編集を開く。
void CCanvas::OnLButtonDblClk(UINT nFlags, CPoint point)
{
if (m_pLayout)
{
CPoint huPt = HuFromClient(point);
CPrintItem* hit = m_pLayout->HitTest(huPt);
if (hit)
{
m_pSelected = hit;
EditSelectedProperties();
Invalidate();
return;
}
}
CWnd::OnLButtonDblClk(nFlags, point);
}
// マウスカーソル形状の制御。選択アイテムのハンドル上では方向に応じた
// リサイズカーソル(↔ ↕ ⤡ ⤢)に切り替える。
BOOL CCanvas::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (nHitTest == HTCLIENT && m_pSelected)
{
CPoint pt;
GetCursorPos(&pt);
ScreenToClient(&pt);
CoordMap map = MakeMap();
CRect devRect = map.MmToDev(m_pSelected->Rect());
int h = CPrintItem::HitHandle(devRect, pt);
if (h >= 0)
{
LPCTSTR cur = IDC_SIZEALL;
switch (h)
{
case 0: case 4: cur = IDC_SIZENWSE; break;
case 2: case 6: cur = IDC_SIZENESW; break;
case 1: case 5: cur = IDC_SIZENS; break;
case 3: case 7: cur = IDC_SIZEWE; break;
}
::SetCursor(::LoadCursor(nullptr, cur));
return TRUE;
}
}
return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
// キー操作。Delete=削除、Ctrl+C/V/D=コピー/貼付/複製、Ctrl+]/[ =前面/背面、
// 矢印=微調整(スナップ時はグリッド幅・通常0.1mm、Shiftで10倍)。
void CCanvas::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
BOOL ctrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
BOOL shift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0;
switch (nChar)
{
case VK_DELETE:
DeleteSelected();
return;
case 'C': if (ctrl) { CopySelected(); return; } break;
case 'V': if (ctrl) { Paste(); return; } break;
case 'D': if (ctrl) { DuplicateSelected(); return; } break;
case VK_OEM_6: if (ctrl) { BringSelectedForward(); return; } break; // ']'
case VK_OEM_4: if (ctrl) { SendSelectedBackward(); return; } break; // '['
case VK_LEFT: case VK_RIGHT: case VK_UP: case VK_DOWN:
if (m_pSelected)
{
int step = m_snap ? m_gridHu : 10; // 10hu = 0.1mm
if (shift) step *= 10;
int dx = 0, dy = 0;
if (nChar == VK_LEFT) dx = -step;
else if (nChar == VK_RIGHT) dx = step;
else if (nChar == VK_UP) dy = -step;
else dy = step;
CRect r = m_pSelected->Rect();
r.OffsetRect(dx, dy);
m_pSelected->SetRect(r);
m_dirty = TRUE;
Invalidate();
return;
}
break;
}
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
// このコントロールが欲しいキー種別を返す。矢印キーがダイアログのフォーカス移動に
// 奪われないよう DLGC_WANTARROWS を返し、矢印での微調整を確実に受け取る。
UINT CCanvas::OnGetDlgCode()
{
return DLGC_WANTARROWS | DLGC_WANTCHARS;
}
// ---- 挿入・編集 ----------------------------------------------------------
// アイテムをレイアウトへ追加し、それを選択状態にして再描画する(挿入系の共通処理)。
void CCanvas::AddAndSelect(CPrintItem* p)
{
m_pLayout->AddItem(p);
m_pSelected = p;
m_dirty = TRUE;
Invalidate();
}
// テキストアイテムを追加。用紙中央に置き、プロパティ入力ダイアログで
// 内容を確定したら追加する(キャンセル時は破棄)。
void CCanvas::InsertText()
{
if (!m_pLayout) return;
CTextItem* p = new CTextItem();
int w = 3000, h = 800;
int x = (m_pLayout->m_paperWidthHu - w) / 2;
int y = (m_pLayout->m_paperHeightHu - h) / 2;
p->SetRect(CRect(x, y, x + w, y + h));
CTextPropsDlg dlg(this);
dlg.m_text = p->m_text;
dlg.m_fontName = p->m_fontName;
dlg.m_pointSize = p->m_pointSize;
dlg.m_align = p->m_align;
dlg.m_bold = p->m_bold;
if (dlg.DoModal() == IDOK)
{
p->m_text = dlg.m_text;
p->m_fontName = dlg.m_fontName;
p->m_pointSize = dlg.m_pointSize;
p->m_align = dlg.m_align;
p->m_bold = dlg.m_bold;
AddAndSelect(p);
}
else
{
delete p;
}
}
// 矩形枠アイテムを追加(用紙の約6割×4割サイズで中央配置)。
void CCanvas::InsertRect()
{
if (!m_pLayout) return;
CShapeItem* p = new CShapeItem();
p->m_shape = 0;
int w = m_pLayout->m_paperWidthHu * 6 / 10;
int h = m_pLayout->m_paperHeightHu * 4 / 10;
int x = (m_pLayout->m_paperWidthHu - w) / 2;
int y = (m_pLayout->m_paperHeightHu - h) / 2;
p->SetRect(CRect(x, y, x + w, y + h));
AddAndSelect(p);
}
// 直線アイテムを追加(用紙中央の水平線)。
void CCanvas::InsertLine()
{
if (!m_pLayout) return;
CShapeItem* p = new CShapeItem();
p->m_shape = 1;
int y = m_pLayout->m_paperHeightHu / 2;
p->SetRect(CRect(m_pLayout->m_paperWidthHu / 10, y,
m_pLayout->m_paperWidthHu * 9 / 10, y));
AddAndSelect(p);
}
// 画像アイテムを追加。ファイル選択→読み込み成功なら用紙中央に半分サイズで配置。
void CCanvas::InsertImage()
{
if (!m_pLayout) return;
CFileDialog dlg(TRUE, nullptr, nullptr,
OFN_FILEMUSTEXIST | OFN_HIDEREADONLY,
_T("画像ファイル (*.png;*.jpg;*.jpeg;*.bmp;*.gif)|*.png;*.jpg;*.jpeg;*.bmp;*.gif|すべて (*.*)|*.*||"),
this);
if (dlg.DoModal() != IDOK)
return;
CImageItem* p = new CImageItem();
if (!p->LoadFromFile(dlg.GetPathName()))
{
delete p;
AfxMessageBox(_T("画像を読み込めませんでした。"));
return;
}
int w = m_pLayout->m_paperWidthHu / 2;
int h = m_pLayout->m_paperHeightHu / 2;
int x = (m_pLayout->m_paperWidthHu - w) / 2;
int y = (m_pLayout->m_paperHeightHu - h) / 2;
p->SetRect(CRect(x, y, x + w, y + h));
AddAndSelect(p);
}
// 選択アイテムのプロパティを編集する。現状はテキストのみ編集ダイアログを持つ。
void CCanvas::EditSelectedProperties()
{
if (!m_pSelected) return;
if (m_pSelected->Type() == ItemType::Text)
{
CTextItem* t = static_cast<CTextItem*>(m_pSelected);
CTextPropsDlg dlg(this);
dlg.m_text = t->m_text;
dlg.m_fontName = t->m_fontName;
dlg.m_pointSize = t->m_pointSize;
dlg.m_align = t->m_align;
dlg.m_bold = t->m_bold;
if (dlg.DoModal() == IDOK)
{
t->m_text = dlg.m_text;
t->m_fontName = dlg.m_fontName;
t->m_pointSize = dlg.m_pointSize;
t->m_align = dlg.m_align;
t->m_bold = dlg.m_bold;
m_dirty = TRUE;
Invalidate();
}
}
else
{
AfxMessageBox(_T("この種類のアイテムには編集可能なプロパティがありません。"));
}
}
// 選択中アイテムを削除する。
void CCanvas::DeleteSelected()
{
if (!m_pSelected || !m_pLayout) return;
m_pLayout->DeleteItem(m_pSelected);
m_pSelected = nullptr;
m_dirty = TRUE;
Invalidate();
}
// ---- コピー / 複製 -------------------------------------------------------
// 選択アイテムをクリップボード(自前保持)へ複製して控える。
void CCanvas::CopySelected()
{
if (!m_pSelected) return;
delete m_pClipboard;
m_pClipboard = m_pSelected->Clone();
}
// クリップボードの内容を 1mm ずらして貼り付け、選択状態にする。
void CCanvas::Paste()
{
if (!m_pClipboard || !m_pLayout) return;
CPrintItem* p = m_pClipboard->Clone();
CRect r = p->Rect();
r.OffsetRect(100, 100); // 1mm ずらして重なりを避ける
p->SetRect(r);
AddAndSelect(p);
}
// 選択アイテムをその場で複製し(1mm ずらし)、複製を選択状態にする。
void CCanvas::DuplicateSelected()
{
if (!m_pSelected || !m_pLayout) return;
CPrintItem* p = m_pSelected->Clone();
CRect r = p->Rect();
r.OffsetRect(100, 100);
p->SetRect(r);
AddAndSelect(p);
}
// ---- z オーダー ----------------------------------------------------------
// 選択アイテムを最前面へ。
void CCanvas::BringSelectedToFront()
{
if (!m_pSelected || !m_pLayout) return;
m_pLayout->BringToFront(m_pSelected);
m_dirty = TRUE;
Invalidate();
}
// 選択アイテムを最背面へ。
void CCanvas::SendSelectedToBack()
{
if (!m_pSelected || !m_pLayout) return;
m_pLayout->SendToBack(m_pSelected);
m_dirty = TRUE;
Invalidate();
}
// 選択アイテムを1つ前面へ。
void CCanvas::BringSelectedForward()
{
if (!m_pSelected || !m_pLayout) return;
m_pLayout->BringForward(m_pSelected);
m_dirty = TRUE;
Invalidate();
}
// 選択アイテムを1つ背面へ。
void CCanvas::SendSelectedBackward()
{
if (!m_pSelected || !m_pLayout) return;
m_pLayout->SendBackward(m_pSelected);
m_dirty = TRUE;
Invalidate();
}
// ---- 整列(用紙/余白の内側矩形を基準) ------------------------------------
// 選択アイテムを基準矩形(余白があれば余白内側、無ければ用紙)に対して整列する。
// how: 0=左 1=水平中央 2=右 3=上 4=垂直中央 5=下。サイズは変えず位置だけ移動。
void CCanvas::AlignSelected(int how)
{
if (!m_pSelected || !m_pLayout) return;
CRect b = m_pLayout->ContentRect();
CRect r = m_pSelected->Rect();
int w = r.Width(), h = r.Height();
switch (how)
{
case 0: r.left = b.left; r.right = r.left + w; break; // 左
case 1: { int cx = (b.left + b.right) / 2; r.left = cx - w / 2; r.right = r.left + w; } break; // 水平中央
case 2: r.right = b.right; r.left = r.right - w; break; // 右
case 3: r.top = b.top; r.bottom = r.top + h; break; // 上
case 4: { int cy = (b.top + b.bottom) / 2; r.top = cy - h / 2; r.bottom = r.top + h; } break; // 垂直中央
case 5: r.bottom = b.bottom; r.top = r.bottom - h; break; // 下
}
m_pSelected->SetRect(r);
m_dirty = TRUE;
Invalidate();
}
// 「新規」「開く」でレイアウトが差し替わった後に呼ぶ。選択解除して再描画する。
void CCanvas::OnLayoutReplaced()
{
m_pSelected = nullptr;
m_dirty = FALSE;
Invalidate();
}
+84
View File
@@ -0,0 +1,84 @@
// Canvas.h : ラベル編集キャンバス(ダイアログ上の子ウィンドウ)
// 旧 CLabelView の編集ロジックを CWnd ベースに移植(スクロール無し・ズーム有り)。
#pragma once
#include "CoordMap.h"
class CLabelLayout;
class CPrintItem;
class CCanvas : public CWnd
{
public:
CCanvas();
virtual ~CCanvas();
BOOL Create(CWnd* pParent, const CRect& rc, UINT id);
void SetLayout(CLabelLayout* p) { m_pLayout = p; m_pSelected = nullptr; }
// ダイアログのボタンから呼ぶ操作
void InsertText();
void InsertRect();
void InsertLine();
void InsertImage();
void EditSelectedProperties();
void DeleteSelected();
void ZoomIn() { SetZoom(m_zoom * 1.25); }
void ZoomOut() { SetZoom(m_zoom / 1.25); }
void FitZoom(); // 用紙がキャンバスに収まる倍率へ
// コピー/複製/Zオーダー/整列(ダイアログ・キーから呼ぶ)
void CopySelected();
void Paste();
void DuplicateSelected();
void BringSelectedToFront();
void SendSelectedToBack();
void BringSelectedForward();
void SendSelectedBackward();
void AlignSelected(int how); // 0=左 1=水平中央 2=右 3=上 4=垂直中央 5=下
// グリッド/スナップ
void SetSnap(BOOL b) { m_snap = b; Invalidate(); }
BOOL GetSnap() const { return m_snap; }
BOOL HasSelection() const { return m_pSelected != nullptr; }
BOOL IsDirty() const { return m_dirty; }
void SetDirty(BOOL b) { m_dirty = b; }
void OnLayoutReplaced(); // New/Open 後の再描画+選択解除
double m_zoom;
protected:
CLabelLayout* m_pLayout;
CPrintItem* m_pSelected;
CPrintItem* m_pClipboard; // コピー保持(自前所有)
BOOL m_dirty;
BOOL m_snap; // グリッドスナップ有効
int m_gridHu; // グリッド間隔(1/100mm)
int SnapHu(int hu) const; // 最近接グリッドへ丸め
enum class Drag { None, Move, Resize };
Drag m_drag;
int m_dragHandle;
CPoint m_dragStartHu;
CRect m_origRect;
static const int kMargin = 14;
CoordMap MakeMap() const;
CoordMap MakeMap(int dpiX, int dpiY) const;
CPoint HuFromClient(CPoint pt);
void SetZoom(double z);
void AddAndSelect(CPrintItem* p);
afx_msg void OnPaint();
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
afx_msg UINT OnGetDlgCode(); // 矢印キー等をキャンバスへ届ける
DECLARE_MESSAGE_MAP()
};
+79
View File
@@ -0,0 +1,79 @@
// CoordMap.h : 物理単位(1/100mm) ⇔ デバイス単位(ピクセル) 変換
//
// 設計の背骨:
// - すべてのジオメトリ(用紙サイズ・各アイテムの矩形)は 1/100 mm の整数で保持する。
// - 画面エディタも印刷(OnPrint)も「同じ」描画コード Draw(CDC&, CoordMap&) を呼ぶ。
// 違いはこの CoordMap が保持する DPI とズーム倍率だけ。
// - これにより、画面(≒96dpi)とサーマルラベルプリンタ(≒203dpi)で
// 同一レイアウトが物理的に正しいサイズで出力される。
//
// 単位系メモ: 1 inch = 25.4 mm = 2540 (1/100mm)
#pragma once
class CoordMap
{
public:
// dpiX/dpiY : 出力デバイスの解像度(GetDeviceCaps(LOGPIXELSX/Y))
// zoom : 画面表示倍率(印刷時は 1.0)
// originDevX/Y : デバイス座標の原点オフセット(スクロール量や印刷可能領域オフセット)
CoordMap(int dpiX = 96, int dpiY = 96, double zoom = 1.0,
int originDevX = 0, int originDevY = 0)
: m_dpiX(dpiX), m_dpiY(dpiY), m_zoom(zoom),
m_originX(originDevX), m_originY(originDevY)
{
}
void SetZoom(double zoom) { m_zoom = zoom; }
double Zoom() const { return m_zoom; }
void SetOrigin(int devX, int devY) { m_originX = devX; m_originY = devY; }
// 1/100mm → デバイス(ピクセル)。原点オフセットとズームを適用。
int MmToDevX(int hu) const { return m_originX + ScaleX(hu); }
int MmToDevY(int hu) const { return m_originY + ScaleY(hu); }
// 寸法(長さ)変換: 原点オフセットを加えない
int LenToDevX(int hu) const { return ScaleX(hu); }
int LenToDevY(int hu) const { return ScaleY(hu); }
CRect MmToDev(const CRect& huRect) const
{
return CRect(MmToDevX(huRect.left), MmToDevY(huRect.top),
MmToDevX(huRect.right), MmToDevY(huRect.bottom));
}
CPoint MmToDev(const CPoint& huPt) const
{
return CPoint(MmToDevX(huPt.x), MmToDevY(huPt.y));
}
// デバイス(ピクセル) → 1/100mm
int DevToMmX(int dev) const { return UnscaleX(dev - m_originX); }
int DevToMmY(int dev) const { return UnscaleY(dev - m_originY); }
CPoint DevToMm(const CPoint& devPt) const
{
return CPoint(DevToMmX(devPt.x), DevToMmY(devPt.y));
}
// ポイント(1pt = 1/72inch) → デバイス上のフォント高さ(論理ピクセル, ズーム込み)
int PointToDevHeight(double pt) const
{
// pt → 1/100mm → デバイス
int hu = (int)(pt * 2540.0 / 72.0 + 0.5);
return LenToDevY(hu);
}
private:
// hu(1/100mm) → デバイス: dev = hu / 2540 * dpi * zoom
int ScaleX(int hu) const { return (int)((double)hu * m_dpiX * m_zoom / 2540.0 + (hu >= 0 ? 0.5 : -0.5)); }
int ScaleY(int hu) const { return (int)((double)hu * m_dpiY * m_zoom / 2540.0 + (hu >= 0 ? 0.5 : -0.5)); }
int UnscaleX(int dev) const { return (int)((double)dev * 2540.0 / (m_dpiX * m_zoom) + (dev >= 0 ? 0.5 : -0.5)); }
int UnscaleY(int dev) const { return (int)((double)dev * 2540.0 / (m_dpiY * m_zoom) + (dev >= 0 ? 0.5 : -0.5)); }
int m_dpiX;
int m_dpiY;
double m_zoom;
int m_originX;
int m_originY;
};
+199
View File
@@ -0,0 +1,199 @@
// ImageItem.cpp
#include "pch.h"
#include "ImageItem.h"
#include <algorithm>
using std::min; // NOMINMAX 下で gdiplus.h が min/max を使うための保険
using std::max;
#include <gdiplus.h>
#pragma comment(lib, "gdiplus.lib")
IMPLEMENT_SERIAL(CImageItem, CPrintItem, 1)
// コンストラクタ。既定は縦横比保持フィット(m_fit=1)。
CImageItem::CImageItem()
: m_fit(1)
{
}
// コピーコンストラクタ。画像バイト列はコピーするが、描画キャッシュ(m_cache)は
// 複製せず必要時に再生成する。Clone() から利用される。
CImageItem::CImageItem(const CImageItem& other)
: CPrintItem(other)
, m_fit(other.m_fit)
{
m_bytes.Copy(other.m_bytes);
// m_cache は複製しない(遅延再生成)
}
// 画像ファイルを読み込み、その生バイト列を m_bytes に取り込む(.otz 埋め込み用)。
// 読み込み妥当性の確認も兼ねて一度デコードする。失敗時は FALSE。
BOOL CImageItem::LoadFromFile(LPCTSTR path)
{
CFile file;
if (!file.Open(path, CFile::modeRead | CFile::shareDenyWrite))
return FALSE;
ULONGLONG len = file.GetLength();
if (len == 0 || len > (ULONGLONG)INT_MAX)
return FALSE;
m_bytes.SetSize((INT_PTR)len);
file.Read(m_bytes.GetData(), (UINT)len);
file.Close();
m_cache.reset();
// 妥当性確認 + 既定サイズ算出のため一度ロード
CImage* img = EnsureImage();
if (img == nullptr)
{
m_bytes.RemoveAll();
return FALSE;
}
return TRUE;
}
// m_bytes から CImage を遅延生成して返す(描画キャッシュ)。
// 既に生成済みならそれを返す。バイト列が無効なら nullptr。
CImage* CImageItem::EnsureImage()
{
if (m_cache && !m_cache->IsNull())
return m_cache.get();
if (m_bytes.GetSize() == 0)
return nullptr;
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)m_bytes.GetSize());
if (!hMem)
return nullptr;
void* p = ::GlobalLock(hMem);
memcpy(p, m_bytes.GetData(), (size_t)m_bytes.GetSize());
::GlobalUnlock(hMem);
IStream* pStream = nullptr;
if (FAILED(::CreateStreamOnHGlobal(hMem, TRUE, &pStream)))
{
::GlobalFree(hMem);
return nullptr;
}
auto img = std::make_shared<CImage>();
HRESULT hr = img->Load(pStream);
pStream->Release(); // fDeleteOnRelease=TRUE のため hMem も解放される
if (FAILED(hr) || img->IsNull())
return nullptr;
m_cache = img;
return m_cache.get();
}
BOOL CImageItem::GetPixelSize(int& w, int& h)
{
CImage* img = EnsureImage();
if (!img) return FALSE;
w = img->GetWidth();
h = img->GetHeight();
return (w > 0 && h > 0);
}
BOOL CImageItem::GetResolutionDpi(double& dx, double& dy)
{
// EnsureImage() 成功中は atlimage が GDI+ を起動済みなので、
// ここでは GdiplusStartup を呼ばず Gdiplus::Bitmap で解像度だけ読む。
if (EnsureImage() == nullptr || m_bytes.GetSize() == 0)
return FALSE;
HGLOBAL hMem = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)m_bytes.GetSize());
if (!hMem) return FALSE;
void* p = ::GlobalLock(hMem);
memcpy(p, m_bytes.GetData(), (size_t)m_bytes.GetSize());
::GlobalUnlock(hMem);
IStream* pStream = nullptr;
if (FAILED(::CreateStreamOnHGlobal(hMem, TRUE, &pStream)))
{
::GlobalFree(hMem);
return FALSE;
}
BOOL ok = FALSE;
{
Gdiplus::Bitmap bmp(pStream, FALSE);
if (bmp.GetLastStatus() == Gdiplus::Ok)
{
Gdiplus::REAL hx = bmp.GetHorizontalResolution();
Gdiplus::REAL hy = bmp.GetVerticalResolution();
if (hx > 0 && hy > 0) { dx = hx; dy = hy; ok = TRUE; }
}
}
pStream->Release(); // fDeleteOnRelease=TRUE のため hMem も解放
return ok;
}
// 画像を矩形内に描画する。m_fit=1 なら縦横比を保って収め、0 なら矩形いっぱいに
// 引き伸ばす。画像が読めない場合は ×印付きのプレースホルダ枠を描く。
void CImageItem::Draw(CDC& dc, const CoordMap& map, bool selected)
{
CRect dr = map.MmToDev(m_rect);
CImage* img = EnsureImage();
if (img)
{
CRect dest = dr;
if (m_fit == 1 && img->GetWidth() > 0 && img->GetHeight() > 0)
{
// 縦横比保持で矩形内にフィット
double sr = (double)img->GetWidth() / img->GetHeight();
double dRatio = (double)dr.Width() / max(1, dr.Height());
if (sr > dRatio)
{
int h = (int)(dr.Width() / sr + 0.5);
dest = CRect(dr.left, dr.top, dr.right, dr.top + h);
}
else
{
int w = (int)(dr.Height() * sr + 0.5);
dest = CRect(dr.left, dr.top, dr.left + w, dr.bottom);
}
}
int oldMode = dc.SetStretchBltMode(HALFTONE);
::SetBrushOrgEx(dc.GetSafeHdc(), 0, 0, nullptr);
img->StretchBlt(dc.GetSafeHdc(), dest, CRect(0, 0, img->GetWidth(), img->GetHeight()), SRCCOPY);
dc.SetStretchBltMode(oldMode);
}
else
{
// 画像なし: プレースホルダ枠 + ×印
CBrush* pOldBrush = (CBrush*)dc.SelectStockObject(NULL_BRUSH);
dc.Rectangle(dr);
dc.MoveTo(dr.left, dr.top); dc.LineTo(dr.right, dr.bottom);
dc.MoveTo(dr.right, dr.top); dc.LineTo(dr.left, dr.bottom);
dc.SelectObject(pOldBrush);
}
if (selected)
DrawSelection(dc, dr);
}
// 自身の複製を生成して返す(画像バイト列ごと複製)。
CPrintItem* CImageItem::Clone() const
{
return new CImageItem(*this);
}
// 永続化。基底(矩形)に続けてフィット方式と画像バイト列を読み書きする。
void CImageItem::Serialize(CArchive& ar)
{
CPrintItem::Serialize(ar);
if (ar.IsStoring())
{
ar << m_fit;
m_bytes.Serialize(ar);
}
else
{
ar >> m_fit;
m_bytes.Serialize(ar);
m_cache.reset();
}
}
+36
View File
@@ -0,0 +1,36 @@
// ImageItem.h : 画像(ロゴ等)印刷アイテム
// 画像バイト列を .otz に埋め込み、ファイル単体で完結させる。
#pragma once
#include "PrintItem.h"
#include <atlimage.h>
#include <memory>
class CImageItem : public CPrintItem
{
DECLARE_SERIAL(CImageItem)
public:
CImageItem();
CImageItem(const CImageItem& other);
CByteArray m_bytes; // 元画像ファイルのバイト列(PNG/JPG/BMP)
int m_fit; // 0=矩形にフィット(縦横比無視), 1=縦横比保持
// ファイルから読み込んでバイト列を取り込む
BOOL LoadFromFile(LPCTSTR path);
// 元画像の画素サイズ(px)。取得できれば TRUE。
BOOL GetPixelSize(int& w, int& h);
// 元画像の埋め込み解像度(DPI)。情報が無ければ FALSE。
BOOL GetResolutionDpi(double& dx, double& dy);
ItemType Type() const override { return ItemType::Image; }
void Draw(CDC& dc, const CoordMap& map, bool selected) override;
CPrintItem* Clone() const override;
void Serialize(CArchive& ar) override;
private:
// バイト列から CImage を遅延生成(描画キャッシュ。シリアライズ対象外)
CImage* EnsureImage();
std::shared_ptr<CImage> m_cache;
};
+278
View File
@@ -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;
}
+63
View File
@@ -0,0 +1,63 @@
// LabelLayout.h : ラベルレイアウトのデータモデル(Doc/View に依存しない)
// 全印刷領域(用紙サイズ)と複数の印刷アイテムを保持し、.otz の保存/読込を行う。
#pragma once
#include "PrintItem.h"
class CImageItem;
class CLabelLayout
{
public:
CLabelLayout();
~CLabelLayout();
// 全印刷領域(用紙サイズ, 1/100mm)
int m_paperWidthHu;
int m_paperHeightHu;
int m_dpi; // 想定プリンタ解像度(プリセット情報)
int m_marginHu; // 四辺一律の内側余白(1/100mm, 0=なし)
// 原紙(位置合わせ用の背景画像)。用紙全体に敷く下絵。
// 画面では常に薄く表示し、印刷は m_printTemplate のときだけ行う。
CImageItem* m_pTemplate; // nullptr = 原紙なし
BOOL m_printTemplate;// 原紙自身を印刷するか(既定 FALSE)
CObArray m_items; // CPrintItem* (描画順 = z オーダー)
void AddItem(CPrintItem* p);
void DeleteItem(CPrintItem* p);
void RemoveAll();
int GetItemCount() const { return (int)m_items.GetCount(); }
CPrintItem* GetItem(int i) const { return (CPrintItem*)m_items.GetAt(i); }
CPrintItem* HitTest(const CPoint& huPt) const; // 最前面優先。無ければ nullptr
// 余白を考慮した内側矩形(整列の基準・ガイド描画用)。余白0なら用紙全体。
CRect ContentRect() const;
// 原紙(背景下絵)操作
BOOL LoadTemplate(LPCTSTR path); // 読込(用紙全体に敷く)。失敗で FALSE
void ClearTemplate(); // 原紙を破棄
CImageItem* Template() const { return m_pTemplate; }
BOOL HasTemplate() const { return m_pTemplate != nullptr; }
void SyncTemplateRect(); // 原紙の矩形を現在の用紙サイズに合わせる
// 原紙の画素サイズ/埋め込みDPI(取得可なら TRUE)
BOOL GetTemplatePixelSize(int& w, int& h) const;
BOOL GetTemplateDpi(double& dx, double& dy) const;
// 原紙の画素数から用紙サイズを設定。useEmbeddedDpi=TRUE で画像の埋め込み
// DPI、FALSE(または埋め込み無し)で m_dpi を基準に px→1/100mm 換算する。
void SetPaperSizeFromTemplate(BOOL useEmbeddedDpi);
// z オーダー操作(m_items 内の位置を入替)
void BringToFront(CPrintItem* p);
void SendToBack(CPrintItem* p);
void BringForward(CPrintItem* p);
void SendBackward(CPrintItem* p);
void Reset(); // 既定の空レイアウトに戻す
void Serialize(CArchive& ar);
BOOL SaveToFile(LPCTSTR path);
BOOL LoadFromFile(LPCTSTR path);
};
+422
View File
@@ -0,0 +1,422 @@
// MainDialog.cpp
#include "pch.h"
#include "AveryDennisonUtil.h"
#include "MainDialog.h"
#include "RenderScene.h"
#include "PaperSizeDlg.h"
// ===== バージョン情報ダイアログ =====
class CAboutDlg : public CDialog
{
public:
CAboutDlg() : CDialog(IDD_ABOUTBOX) {}
enum { IDD = IDD_ABOUTBOX };
protected:
void DoDataExchange(CDataExchange* pDX) override { CDialog::DoDataExchange(pDX); }
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()
// 子キャンバスのウィンドウ ID(リソース上の IDC_CANVAS とは別)
static const UINT kCanvasId = 1101;
static LPCTSTR kFilter =
_T("Otozai ラベル (*.otz)|*.otz|すべてのファイル (*.*)|*.*||");
BEGIN_MESSAGE_MAP(CMainDialog, CDialog)
ON_COMMAND(ID_FILE_NEW, &CMainDialog::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, &CMainDialog::OnFileOpen)
ON_COMMAND(ID_FILE_SAVE, &CMainDialog::OnFileSave)
ON_COMMAND(ID_FILE_SAVE_AS, &CMainDialog::OnFileSaveAs)
ON_COMMAND(ID_INSERT_TEXT, &CMainDialog::OnInsertText)
ON_COMMAND(ID_INSERT_IMAGE, &CMainDialog::OnInsertImage)
ON_COMMAND(ID_INSERT_RECT, &CMainDialog::OnInsertRect)
ON_COMMAND(ID_INSERT_LINE, &CMainDialog::OnInsertLine)
ON_COMMAND(ID_EDIT_PROPERTIES, &CMainDialog::OnEditProperties)
ON_COMMAND(ID_EDIT_DELETE_ITEM, &CMainDialog::OnEditDeleteItem)
ON_COMMAND(ID_PAPER_SIZE, &CMainDialog::OnPaperSize)
ON_COMMAND(ID_VIEW_ZOOMIN, &CMainDialog::OnViewZoomIn)
ON_COMMAND(ID_VIEW_ZOOMOUT, &CMainDialog::OnViewZoomOut)
ON_COMMAND(ID_FILE_PRINT, &CMainDialog::OnFilePrint)
ON_COMMAND(ID_APP_ABOUT, &CMainDialog::OnAppAbout)
ON_COMMAND(ID_ITEM_COPY, &CMainDialog::OnItemCopy)
ON_COMMAND(ID_ITEM_PASTE, &CMainDialog::OnItemPaste)
ON_COMMAND(ID_ITEM_DUPLICATE, &CMainDialog::OnItemDuplicate)
ON_COMMAND(ID_ITEM_FRONT, &CMainDialog::OnItemFront)
ON_COMMAND(ID_ITEM_BACK, &CMainDialog::OnItemBack)
ON_COMMAND(ID_ALIGN_MENU, &CMainDialog::OnAlignMenu)
ON_COMMAND_RANGE(ID_ALIGN_LEFT, ID_ALIGN_BOTTOM, &CMainDialog::OnAlignCmd)
ON_BN_CLICKED(IDC_CHECK_GRID, &CMainDialog::OnToggleGrid)
ON_COMMAND(ID_TEMPLATE_LOAD, &CMainDialog::OnTemplateLoad)
ON_COMMAND(ID_TEMPLATE_CLEAR, &CMainDialog::OnTemplateClear)
ON_BN_CLICKED(IDC_CHECK_PRINT_TEMPLATE, &CMainDialog::OnTogglePrintTemplate)
END_MESSAGE_MAP()
// コンストラクタ。メインダイアログのリソース ID を指定して生成。
CMainDialog::CMainDialog(CWnd* pParent)
: CDialog(IDD_MAIN_DIALOG, pParent)
{
}
// コントロールと変数の関連付け(本ダイアログは直接の DDX 変数は持たない)。
void CMainDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
// ダイアログ初期化。プレースホルダ位置に編集キャンバスを生成し、
// チェックボックスの初期状態をモデルへ合わせる。
BOOL CMainDialog::OnInitDialog()
{
CDialog::OnInitDialog();
SetIcon(AfxGetApp()->LoadStandardIcon(IDI_APPLICATION), TRUE);
SetIcon(AfxGetApp()->LoadStandardIcon(IDI_APPLICATION), FALSE);
// プレースホルダ(IDC_CANVAS)の矩形にキャンバスを生成
CWnd* pHolder = GetDlgItem(IDC_CANVAS);
CRect rc;
if (pHolder)
{
pHolder->GetWindowRect(rc);
ScreenToClient(rc);
pHolder->ShowWindow(SW_HIDE);
}
else
{
rc = CRect(80, 10, 460, 300);
}
m_canvas.SetLayout(&m_layout);
m_canvas.Create(this, rc, kCanvasId);
m_canvas.FitZoom();
// グリッド/スナップのチェック状態をキャンバスの既定に合わせる
CButton* pGrid = (CButton*)GetDlgItem(IDC_CHECK_GRID);
if (pGrid) pGrid->SetCheck(m_canvas.GetSnap() ? BST_CHECKED : BST_UNCHECKED);
UpdateTemplateCheck();
UpdateCaption();
return TRUE;
}
// タイトルバーを「ファイル名 - AveryDennisonUtil」に更新する(未保存は「無題」)。
void CMainDialog::UpdateCaption()
{
CString name = m_path.IsEmpty() ? CString(_T("無題")) : m_path;
if (!m_path.IsEmpty())
{
int slash = m_path.ReverseFind(_T('\\'));
if (slash >= 0) name = m_path.Mid(slash + 1);
}
CString title;
title.Format(_T("%s - AveryDennisonUtil"), (LPCTSTR)name);
SetWindowText(title);
}
// 未保存の変更がある場合に保存確認する。続行可なら TRUE、キャンセルなら FALSE。
// 新規/開く/終了の前に呼んで作業内容の消失を防ぐ。
BOOL CMainDialog::ConfirmDiscardIfDirty()
{
if (!m_canvas.IsDirty())
return TRUE;
int r = AfxMessageBox(_T("変更を保存しますか?"), MB_YESNOCANCEL | MB_ICONQUESTION);
if (r == IDCANCEL) return FALSE;
if (r == IDYES) return DoSave(FALSE);
return TRUE;
}
// Enter キーでダイアログが閉じないように無効化(編集中の誤終了防止)。
void CMainDialog::OnOK()
{
// Enter キーでダイアログを閉じない(編集を継続)
}
// 終了(×ボタン/Esc/「終了」)。未保存なら確認してから閉じる。
void CMainDialog::OnCancel()
{
if (!ConfirmDiscardIfDirty())
return;
CDialog::OnCancel();
}
// ===== ファイル =====
// 「新規」。未保存確認の後、レイアウトを初期化して表示を更新する。
void CMainDialog::OnFileNew()
{
if (!ConfirmDiscardIfDirty()) return;
m_layout.Reset();
m_path.Empty();
m_canvas.OnLayoutReplaced();
m_canvas.FitZoom();
UpdateTemplateCheck();
UpdateCaption();
}
// 「開く」。.otz を選んで読み込み、表示・チェック状態・タイトルを更新する。
void CMainDialog::OnFileOpen()
{
if (!ConfirmDiscardIfDirty()) return;
CFileDialog dlg(TRUE, _T("otz"), nullptr,
OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, kFilter, this);
if (dlg.DoModal() != IDOK) return;
if (!m_layout.LoadFromFile(dlg.GetPathName()))
{
AfxMessageBox(_T("ファイルを開けませんでした。"));
m_layout.Reset();
}
m_path = dlg.GetPathName();
m_canvas.OnLayoutReplaced();
m_canvas.FitZoom();
UpdateTemplateCheck();
UpdateCaption();
}
void CMainDialog::OnFileSave() { DoSave(FALSE); } // 「保存」(既存パスへ上書き)
void CMainDialog::OnFileSaveAs(){ DoSave(TRUE); } // 「名前を付けて保存」
// 保存の実処理。bForceDialog=TRUE またはパス未設定のときは保存ダイアログを出す。
// 成功で TRUE。保存後はダーティ状態を解除しタイトルを更新する。
BOOL CMainDialog::DoSave(BOOL bForceDialog)
{
CString path = m_path;
if (bForceDialog || path.IsEmpty())
{
CFileDialog dlg(FALSE, _T("otz"), m_path.IsEmpty() ? _T("label.otz") : (LPCTSTR)m_path,
OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY, kFilter, this);
if (dlg.DoModal() != IDOK) return FALSE;
path = dlg.GetPathName();
}
if (!m_layout.SaveToFile(path))
{
AfxMessageBox(_T("保存に失敗しました。"));
return FALSE;
}
m_path = path;
m_canvas.SetDirty(FALSE);
UpdateCaption();
return TRUE;
}
// ===== 挿入・編集(キャンバスへ委譲) =====
void CMainDialog::OnInsertText() { m_canvas.InsertText(); }
void CMainDialog::OnInsertImage() { m_canvas.InsertImage(); }
void CMainDialog::OnInsertRect() { m_canvas.InsertRect(); }
void CMainDialog::OnInsertLine() { m_canvas.InsertLine(); }
void CMainDialog::OnEditProperties() { m_canvas.EditSelectedProperties(); }
void CMainDialog::OnEditDeleteItem() { m_canvas.DeleteSelected(); }
void CMainDialog::OnViewZoomIn() { m_canvas.ZoomIn(); }
void CMainDialog::OnViewZoomOut() { m_canvas.ZoomOut(); }
// 「用紙サイズ」。現在値をダイアログに渡し、OK なら用紙寸法・DPI・余白を反映する。
void CMainDialog::OnPaperSize()
{
CPaperSizeDlg dlg(this);
dlg.m_widthMm = m_layout.m_paperWidthHu / 100.0;
dlg.m_heightMm = m_layout.m_paperHeightHu / 100.0;
dlg.m_dpi = m_layout.m_dpi;
dlg.m_marginMm = m_layout.m_marginHu / 100.0;
if (dlg.DoModal() == IDOK)
{
m_layout.m_paperWidthHu = (int)(dlg.m_widthMm * 100 + 0.5);
m_layout.m_paperHeightHu = (int)(dlg.m_heightMm * 100 + 0.5);
m_layout.m_dpi = dlg.m_dpi;
m_layout.m_marginHu = (int)(dlg.m_marginMm * 100 + 0.5);
m_layout.SyncTemplateRect();
m_canvas.SetDirty(TRUE);
m_canvas.FitZoom();
m_canvas.Invalidate();
}
}
// ===== 印刷(GDI 直接印刷) =====
// 「印刷」。プリンタ選択ダイアログ→プリンタ DC を取得し、画面と同じ
// Render::DrawLayout で 1 ページ印刷する(StartDoc/StartPage/EndPage/EndDoc)。
void CMainDialog::OnFilePrint()
{
CPrintDialog pd(FALSE,
PD_RETURNDC | PD_USEDEVMODECOPIES | PD_NOPAGENUMS | PD_NOSELECTION | PD_ALLPAGES,
this);
if (pd.DoModal() != IDOK)
return;
HDC hdc = pd.GetPrinterDC();
if (!hdc)
{
AfxMessageBox(_T("プリンタ DC を取得できませんでした。"));
return;
}
CDC dc;
dc.Attach(hdc);
DOCINFO di;
ZeroMemory(&di, sizeof(di));
di.cbSize = sizeof(di);
di.lpszDocName = _T("AveryDennisonUtil");
if (dc.StartDoc(&di) > 0)
{
if (dc.StartPage() > 0)
{
int dpiX = dc.GetDeviceCaps(LOGPIXELSX);
int dpiY = dc.GetDeviceCaps(LOGPIXELSY);
// プリンタ DC の原点(0,0)は「印字可能領域」の左上。ラベル mm(0,0) を
// そこに合わせる(印字可能領域の考慮)。物理マージン分を引くと逆に
// 非印字領域へはみ出して切れるため、オフセットは加えない。
CoordMap map(dpiX, dpiY, 1.0, 0, 0); // 実 DPI・原点(0,0)・等倍
Render::DrawLayout(dc, m_layout, map, /*screen=*/false, nullptr);
dc.EndPage();
}
dc.EndDoc();
}
dc.Detach();
::DeleteDC(hdc);
}
// 「バージョン情報」ダイアログを表示する。
void CMainDialog::OnAppAbout()
{
CAboutDlg dlg;
dlg.DoModal();
}
// ===== 編集操作(キャンバスへ委譲) =====
void CMainDialog::OnItemCopy() { m_canvas.CopySelected(); }
void CMainDialog::OnItemPaste() { m_canvas.Paste(); }
void CMainDialog::OnItemDuplicate() { m_canvas.DuplicateSelected(); }
void CMainDialog::OnItemFront() { m_canvas.BringSelectedToFront(); }
void CMainDialog::OnItemBack() { m_canvas.SendSelectedToBack(); }
// 「整列...」ボタン。左/中央/右・上/中/下のポップアップメニューを出す。
// 選択された項目は ON_COMMAND_RANGE 経由で OnAlignCmd に届く。
void CMainDialog::OnAlignMenu()
{
if (!m_canvas.HasSelection())
{
AfxMessageBox(_T("整列するアイテムを選択してください。"));
return;
}
CMenu menu;
menu.CreatePopupMenu();
menu.AppendMenu(MF_STRING, ID_ALIGN_LEFT, _T("左揃え"));
menu.AppendMenu(MF_STRING, ID_ALIGN_HCENTER, _T("左右中央"));
menu.AppendMenu(MF_STRING, ID_ALIGN_RIGHT, _T("右揃え"));
menu.AppendMenu(MF_SEPARATOR, 0, (LPCTSTR)nullptr);
menu.AppendMenu(MF_STRING, ID_ALIGN_TOP, _T("上揃え"));
menu.AppendMenu(MF_STRING, ID_ALIGN_VCENTER, _T("上下中央"));
menu.AppendMenu(MF_STRING, ID_ALIGN_BOTTOM, _T("下揃え"));
CRect rc;
GetDlgItem(ID_ALIGN_MENU)->GetWindowRect(&rc);
menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON,
rc.right, rc.top, this);
}
// 整列メニュー項目のハンドラ。ID の連番を how(0..5)に変換して整列を実行。
void CMainDialog::OnAlignCmd(UINT nID)
{
m_canvas.AlignSelected((int)(nID - ID_ALIGN_LEFT));
}
// 「グリッド/スナップ」チェックの切替をキャンバスへ反映する。
void CMainDialog::OnToggleGrid()
{
CButton* p = (CButton*)GetDlgItem(IDC_CHECK_GRID);
if (p) m_canvas.SetSnap(p->GetCheck() == BST_CHECKED);
}
// ===== 原紙(背景下絵) =====
// 「原紙を印刷」チェックの表示をモデル(m_printTemplate)に合わせる。
void CMainDialog::UpdateTemplateCheck()
{
CButton* p = (CButton*)GetDlgItem(IDC_CHECK_PRINT_TEMPLATE);
if (p) p->SetCheck(m_layout.m_printTemplate ? BST_CHECKED : BST_UNCHECKED);
}
// 「原紙...」ボタン。画像を読み込み、続いて用紙サイズを原紙に合わせるか
// (埋め込みDPI / 用紙DPI / 変更しない)をダイアログで選ばせる。
void CMainDialog::OnTemplateLoad()
{
CFileDialog dlg(TRUE, nullptr, nullptr,
OFN_FILEMUSTEXIST | OFN_HIDEREADONLY,
_T("画像ファイル (*.png;*.jpg;*.jpeg;*.bmp;*.gif)|*.png;*.jpg;*.jpeg;*.bmp;*.gif|すべて (*.*)|*.*||"),
this);
if (dlg.DoModal() != IDOK) return;
if (!m_layout.LoadTemplate(dlg.GetPathName()))
{
AfxMessageBox(_T("原紙を読み込めませんでした。"));
return;
}
// 用紙サイズを原紙ファイルのサイズに合わせる(px→mm 換算の基準を選択)
int pw = 0, ph = 0;
if (m_layout.GetTemplatePixelSize(pw, ph) && pw > 0 && ph > 0)
{
int pdpi = m_layout.m_dpi;
double papW = pw * 25.4 / pdpi, papH = ph * 25.4 / pdpi;
double edx = 0, edy = 0;
BOOL hasEmbed = m_layout.GetTemplateDpi(edx, edy);
CString msg;
int r;
if (hasEmbed)
{
double embW = pw * 25.4 / edx, embH = ph * 25.4 / edy;
msg.Format(
_T("用紙サイズを原紙に合わせます。\n\n画素: %d × %d px\n\n")
_T("[はい] 画像の埋め込みDPI %.0f dpi → %.1f × %.1f mm\n")
_T("[いいえ] 用紙のDPI %d dpi → %.1f × %.1f mm\n")
_T("[キャンセル] 用紙サイズは変更しない"),
pw, ph, edx, embW, embH, pdpi, papW, papH);
r = AfxMessageBox(msg, MB_YESNOCANCEL | MB_ICONQUESTION);
if (r == IDYES) m_layout.SetPaperSizeFromTemplate(TRUE);
else if (r == IDNO) m_layout.SetPaperSizeFromTemplate(FALSE);
// IDCANCEL: 用紙サイズは変更しない(原紙は現用紙に伸縮表示)
}
else
{
msg.Format(
_T("用紙サイズを原紙に合わせます。\n(画像に埋め込みDPI情報がありません)\n\n")
_T("画素: %d × %d px\n\n")
_T("[はい] 用紙のDPI %d dpi → %.1f × %.1f mm で合わせる\n")
_T("[いいえ] 用紙サイズは変更しない"),
pw, ph, pdpi, papW, papH);
r = AfxMessageBox(msg, MB_YESNO | MB_ICONQUESTION);
if (r == IDYES) m_layout.SetPaperSizeFromTemplate(FALSE);
}
}
m_canvas.SetDirty(TRUE);
m_canvas.FitZoom();
m_canvas.Invalidate();
}
// 「消去」ボタン。読み込んだ原紙を破棄して再描画する。
void CMainDialog::OnTemplateClear()
{
if (!m_layout.HasTemplate()) return;
m_layout.ClearTemplate();
m_canvas.SetDirty(TRUE);
m_canvas.Invalidate();
}
// 「原紙を印刷」チェックの切替をモデル(m_printTemplate)へ反映する。
void CMainDialog::OnTogglePrintTemplate()
{
CButton* p = (CButton*)GetDlgItem(IDC_CHECK_PRINT_TEMPLATE);
if (p)
{
m_layout.m_printTemplate = (p->GetCheck() == BST_CHECKED);
m_canvas.SetDirty(TRUE);
}
}
+57
View File
@@ -0,0 +1,57 @@
// MainDialog.h : メイン ダイアログ(ダイアログベース アプリの主ウィンドウ)
#pragma once
#include "resource.h"
#include "LabelLayout.h"
#include "Canvas.h"
class CMainDialog : public CDialog
{
public:
CMainDialog(CWnd* pParent = nullptr);
enum { IDD = IDD_MAIN_DIALOG };
protected:
CLabelLayout m_layout; // データモデル
CCanvas m_canvas; // 編集キャンバス(子ウィンドウ)
CString m_path; // 現在の .otz パス(空 = 無題)
void DoDataExchange(CDataExchange* pDX) override;
BOOL OnInitDialog() override;
void OnOK() override; // Enter での誤終了を防ぐ(何もしない)
void OnCancel() override; // 終了(保存確認)
void UpdateCaption();
void UpdateTemplateCheck(); // 「原紙を印刷」チェックをモデルに合わせる
BOOL ConfirmDiscardIfDirty();
BOOL DoSave(BOOL bForceDialog);
afx_msg void OnFileNew();
afx_msg void OnFileOpen();
afx_msg void OnFileSave();
afx_msg void OnFileSaveAs();
afx_msg void OnInsertText();
afx_msg void OnInsertImage();
afx_msg void OnInsertRect();
afx_msg void OnInsertLine();
afx_msg void OnEditProperties();
afx_msg void OnEditDeleteItem();
afx_msg void OnPaperSize();
afx_msg void OnViewZoomIn();
afx_msg void OnViewZoomOut();
afx_msg void OnFilePrint();
afx_msg void OnAppAbout();
afx_msg void OnItemCopy();
afx_msg void OnItemPaste();
afx_msg void OnItemDuplicate();
afx_msg void OnItemFront();
afx_msg void OnItemBack();
afx_msg void OnAlignMenu();
afx_msg void OnAlignCmd(UINT nID);
afx_msg void OnToggleGrid();
afx_msg void OnTemplateLoad();
afx_msg void OnTemplateClear();
afx_msg void OnTogglePrintTemplate();
DECLARE_MESSAGE_MAP()
};
+76
View File
@@ -0,0 +1,76 @@
// PaperSizeDlg.cpp
#include "pch.h"
#include "AveryDennisonUtil.h"
#include "PaperSizeDlg.h"
IMPLEMENT_DYNAMIC(CPaperSizeDlg, CDialog)
// ロールラベルのよく使う寸法プリセット(幅mm, 高さmm, dpi)
struct Preset { LPCTSTR name; double w; double h; int dpi; };
static const Preset kPresets[] = {
{ _T("ロールラベル 40×30mm (203dpi)"), 40, 30, 203 },
{ _T("ロールラベル 50×30mm (203dpi)"), 50, 30, 203 },
{ _T("ロールラベル 32×25mm (203dpi)"), 32, 25, 203 },
{ _T("ロールラベル 60×40mm (203dpi)"), 60, 40, 203 },
{ _T("レシート 58mm幅 (203dpi)"), 58, 40, 203 },
{ _T("カスタム"), 0, 0, 0 },
};
static const int kPresetCount = sizeof(kPresets) / sizeof(kPresets[0]);
// コンストラクタ。各値を既定(40×30mm, 203dpi, 余白0)で初期化。
CPaperSizeDlg::CPaperSizeDlg(CWnd* pParent)
: CDialog(IDD_PAPER_SIZE, pParent)
, m_widthMm(40.0)
, m_heightMm(30.0)
, m_dpi(203)
, m_marginMm(0.0)
{
}
// 入力欄と各メンバ変数の関連付け+範囲検証(幅/高さ/DPI/余白)。
void CPaperSizeDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT_WIDTH, m_widthMm);
DDV_MinMaxDouble(pDX, m_widthMm, 1.0, 2000.0);
DDX_Text(pDX, IDC_EDIT_HEIGHT, m_heightMm);
DDV_MinMaxDouble(pDX, m_heightMm, 1.0, 2000.0);
DDX_Text(pDX, IDC_EDIT_DPI, m_dpi);
DDV_MinMaxInt(pDX, m_dpi, 50, 1200);
DDX_Text(pDX, IDC_EDIT_MARGIN, m_marginMm);
DDV_MinMaxDouble(pDX, m_marginMm, 0.0, 100.0);
}
// 初期化。プリセット コンボにロールラベル等の候補を流し込み、既定で「カスタム」を選ぶ。
BOOL CPaperSizeDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CComboBox* pc = (CComboBox*)GetDlgItem(IDC_COMBO_PRESET);
if (pc)
{
for (int i = 0; i < kPresetCount; ++i)
pc->AddString(kPresets[i].name);
pc->SetCurSel(kPresetCount - 1); // 既定はカスタム
}
return TRUE;
}
// プリセット選択変更時。選んだ寸法/DPI を各入力欄へ反映する(カスタムは現状維持)。
void CPaperSizeDlg::OnPresetChanged()
{
CComboBox* pc = (CComboBox*)GetDlgItem(IDC_COMBO_PRESET);
if (!pc) return;
int sel = pc->GetCurSel();
if (sel < 0 || sel >= kPresetCount) return;
const Preset& ps = kPresets[sel];
if (ps.w <= 0) return; // カスタムは現状値を維持
m_widthMm = ps.w;
m_heightMm = ps.h;
m_dpi = ps.dpi;
UpdateData(FALSE);
}
BEGIN_MESSAGE_MAP(CPaperSizeDlg, CDialog)
ON_CBN_SELCHANGE(IDC_COMBO_PRESET, &CPaperSizeDlg::OnPresetChanged)
END_MESSAGE_MAP()
+22
View File
@@ -0,0 +1,22 @@
// PaperSizeDlg.h : 全印刷領域(用紙サイズ)設定ダイアログ
#pragma once
#include "resource.h"
class CPaperSizeDlg : public CDialog
{
DECLARE_DYNAMIC(CPaperSizeDlg)
public:
CPaperSizeDlg(CWnd* pParent = nullptr);
enum { IDD = IDD_PAPER_SIZE };
double m_widthMm;
double m_heightMm;
int m_dpi;
double m_marginMm;
protected:
void DoDataExchange(CDataExchange* pDX) override;
BOOL OnInitDialog() override;
afx_msg void OnPresetChanged();
DECLARE_MESSAGE_MAP()
};
+109
View File
@@ -0,0 +1,109 @@
// PrintItem.cpp
#include "pch.h"
#include "PrintItem.h"
IMPLEMENT_DYNAMIC(CPrintItem, CObject)
// 選択ハンドルの一辺(デバイスピクセル)
static const int kHandleSize = 7;
// コンストラクタ。矩形を空(0,0,0,0)で初期化。
CPrintItem::CPrintItem()
: m_rect(0, 0, 0, 0)
{
}
CPrintItem::~CPrintItem()
{
}
// 当たり判定。指定座標(1/100mm)がこのアイテムの矩形内なら TRUE。
// 既定は矩形の内側判定。直線など特殊な形状は派生側で上書きできる。
BOOL CPrintItem::HitTest(const CPoint& huPt) const
{
return m_rect.PtInRect(huPt);
}
// 共通の永続化処理。矩形(left/top/right/bottom)だけを保存/復元する。
// 派生クラスは自分の Serialize で本関数を呼んでから固有データを読み書きする。
void CPrintItem::Serialize(CArchive& ar)
{
CObject::Serialize(ar);
if (ar.IsStoring())
{
ar << (int)m_rect.left << (int)m_rect.top
<< (int)m_rect.right << (int)m_rect.bottom;
}
else
{
int l, t, r, b;
ar >> l >> t >> r >> b;
m_rect.SetRect(l, t, r, b);
}
}
// 8 個のハンドル(四隅 + 各辺中央)の中心座標
static CPoint HandleCenter(const CRect& dr, int i)
{
int mx = (dr.left + dr.right) / 2;
int my = (dr.top + dr.bottom) / 2;
switch (i)
{
case 0: return CPoint(dr.left, dr.top); // 左上
case 1: return CPoint(mx, dr.top); // 上中央
case 2: return CPoint(dr.right, dr.top); // 右上
case 3: return CPoint(dr.right, my); // 右中央
case 4: return CPoint(dr.right, dr.bottom); // 右下
case 5: return CPoint(mx, dr.bottom); // 下中央
case 6: return CPoint(dr.left, dr.bottom); // 左下
case 7: return CPoint(dr.left, my); // 左中央
}
return CPoint(0, 0);
}
// index 番目(0..7)のハンドルが占める小矩形(デバイス座標)を返す。
CRect CPrintItem::HandleRectAt(const CRect& devRect, int index)
{
CPoint c = HandleCenter(devRect, index);
int h = kHandleSize / 2;
return CRect(c.x - h, c.y - h, c.x + h + 1, c.y + h + 1);
}
// 選択中アイテムの装飾(破線枠 + 8個のサイズ変更ハンドル)を描画する。
// devRect はアイテム矩形をデバイス座標へ変換したもの。
void CPrintItem::DrawSelection(CDC& dc, const CRect& devRect)
{
// 破線の選択枠
CPen pen(PS_DOT, 1, RGB(0, 0, 255));
CPen* pOld = dc.SelectObject(&pen);
int oldBk = dc.SetBkMode(TRANSPARENT);
CBrush* pOldBrush = (CBrush*)dc.SelectStockObject(NULL_BRUSH);
dc.Rectangle(devRect);
dc.SelectObject(pOldBrush);
dc.SetBkMode(oldBk);
dc.SelectObject(pOld);
// ハンドル(白塗り+黒枠)
CBrush white(RGB(255, 255, 255));
CPen black(PS_SOLID, 1, RGB(0, 0, 0));
CPen* pOld2 = dc.SelectObject(&black);
CBrush* pOld3 = dc.SelectObject(&white);
for (int i = 0; i < 8; ++i)
dc.Rectangle(HandleRectAt(devRect, i));
dc.SelectObject(pOld3);
dc.SelectObject(pOld2);
}
// devPt(デバイス座標)がどのハンドル上にあるかを返す。0..7、無ければ -1。
// 判定矩形を少し広げて掴みやすくしている。
int CPrintItem::HitHandle(const CRect& devRect, const CPoint& devPt)
{
for (int i = 0; i < 8; ++i)
{
CRect hr = HandleRectAt(devRect, i);
hr.InflateRect(2, 2); // 掴みやすく
if (hr.PtInRect(devPt))
return i;
}
return -1;
}
+49
View File
@@ -0,0 +1,49 @@
// PrintItem.h : 印刷アイテムの抽象基底クラス
//
// 各印刷領域(ゾーン)を表す。位置・サイズは 1/100mm の CRect で保持。
// 描画は Draw() ただ1つに集約し、画面エディタと印刷で共有する。
#pragma once
#include "CoordMap.h"
enum class ItemType { Text, Rect, Line, Image };
class CPrintItem : public CObject
{
DECLARE_DYNAMIC(CPrintItem) // 抽象基底: SERIAL ではなく DYNAMIC(具象クラスのみ SERIAL)
public:
CPrintItem();
virtual ~CPrintItem();
// CObject のコピーコンストラクタはアクセス不可のため、明示的に提供する
// (派生クラスの暗黙コピーコンストラクタ/Clone() が利用する)
CPrintItem(const CPrintItem& src) : CObject(), m_rect(src.m_rect) {}
CPrintItem& operator=(const CPrintItem& src) { m_rect = src.m_rect; return *this; }
// --- 形状(1/100mm) ---
CRect m_rect; // 正規化済み(left<=right, top<=bottom)を前提
void SetRect(const CRect& r) { m_rect = r; m_rect.NormalizeRect(); }
const CRect& Rect() const { return m_rect; }
// --- 種別 ---
virtual ItemType Type() const = 0;
// --- 描画(画面・印刷で共有) ---
// selected が true のとき、画面エディタでは選択枠/ハンドルを描く(印刷時は false)。
virtual void Draw(CDC& dc, const CoordMap& map, bool selected) = 0;
// --- 当たり判定(1/100mm 座標) ---
virtual BOOL HitTest(const CPoint& huPt) const;
// --- 複製 ---
virtual CPrintItem* Clone() const = 0;
// --- 永続化 ---
virtual void Serialize(CArchive& ar) override;
// 選択ハンドルの描画(共通)。ハンドルは 8 個(四隅+各辺中央)。
static void DrawSelection(CDC& dc, const CRect& devRect);
// ハンドル位置(デバイス座標)を取得。index 0..7。見つからなければ false。
static int HitHandle(const CRect& devRect, const CPoint& devPt);
static CRect HandleRectAt(const CRect& devRect, int index);
};
+70
View File
@@ -0,0 +1,70 @@
# AveryDennisonUtil
ラベル/レシートの**レイアウトを画面上で設計し、Windows プリンターへ印刷**する MFC (VC++) デスクトップアプリ。
高価な中華製ラベルソフトの置き換えを目的とする。**ダイアログベース**アプリ(左側にボタン列、右側に編集キャンバス)。
## できること
- 全印刷領域(用紙/ラベルサイズ)の確定(`用紙サイズ` ボタン。ロールラベルのプリセット + 余白指定)
- 複数の印刷領域(ゾーン)の配置・サイズ変更(ドラッグ移動 / ハンドルでリサイズ)
- 各領域に印刷するデータの入力
- テキスト(フォント・サイズ・揃え・太字)
- 矩形枠 / 直線(区切り)
- 画像(PNG/JPG/BMP、`.otz` に埋め込み)
- 編集操作
- コピー / 貼付け / 複製(`Ctrl+C` / `Ctrl+V` / `Ctrl+D`)
- 重なり順(`前面` / `背面` ボタン、`Ctrl+]` / `Ctrl+[` で前後へ)
- 整列(`整列...` ボタン → 用紙/余白を基準に 左右中央/上下中央など)
- グリッド/スナップ(`グリッド/スナップ` チェック、1mm グリッド。矢印キーで微調整、`Shift` で大きく)
- 原紙(位置合わせ用の背景下絵)
- `原紙...` ボタンで画像を読み込み、用紙全体に敷く(画面では薄く表示し、上に載せるアイテムの位置合わせを容易にする)
- 読み込み時に **用紙サイズを原紙ファイルのサイズに合わせる**。px→mm 換算の基準を選択可能:
- 画像の埋め込みDPI(ファイル本来の実寸)/ 用紙のDPI設定 / 変更しない
- `原紙を印刷` チェックで、原紙自身を印刷する/しないを選択(既定 = 印刷しない=既存の印刷済みラベルに重ねる用途)
- `消去` で原紙を除去。原紙画像は `.otz` に埋め込み保存
- Windows プリンターへ印刷(GDI、`CPrintDialog` 経由の直接印刷。ラベル mm(0,0) を印字可能領域の左上に合わせて出力)
- レイアウトの保存・再読込(`*.otz`。版数つきヘッダで前方/後方互換に配慮)
## アーキテクチャ(要点)
- すべてのジオメトリは **1/100 mm の整数**で保持(ピクセルで持たない)。
- 描画は各アイテムの `Draw(CDC&, const CoordMap&)` ただ 1 つに集約し、
**画面エディタと印刷で同一コードを共有**。違いは [`CoordMap`](CoordMap.h) が持つ DPI とズームだけ。
- これにより画面(≒96dpi)とサーマルラベル(≒203dpi)で同一レイアウトが
物理的に正しいサイズで出力される。
| 役割 | クラス |
|------|--------|
| アプリ(ダイアログ起動のみ) | `CAveryDennisonUtilApp` ([AveryDennisonUtil.cpp](AveryDennisonUtil.cpp)) |
| メイン ダイアログ(ボタン・ファイル・印刷) | `CMainDialog : CDialog` ([MainDialog.cpp](MainDialog.cpp)) |
| 編集キャンバス(子ウィンドウ・マウス操作) | `CCanvas : CWnd` ([Canvas.cpp](Canvas.cpp)) |
| データモデル(用紙+アイテム列・.otz 保存) | `CLabelLayout` ([LabelLayout.cpp](LabelLayout.cpp)) |
| 共有描画(画面・印刷) | `Render::DrawLayout` ([RenderScene.cpp](RenderScene.cpp)) |
| 単位変換 | `CoordMap` ([CoordMap.h](CoordMap.h)) |
| アイテム | `CPrintItem` 派生:`CTextItem` / `CShapeItem` / `CImageItem` |
## ビルド / デバッグ
- VS2022 (v143) / x64 / **MFC・CRT は静的リンク**。単一 exe で外部 DLL に依存しないため、
**Visual Studio 2022 で `AveryDennisonUtil.sln` を開き、構成 Debug(または Release)/x64 を選んで
F5(デバッグ実行)するだけで動く**(Debug 用 DLL の配置は不要)。
- コマンドラインからビルドする場合:
```
& "C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe" `
AveryDennisonUtil.sln /p:Configuration=Debug /p:Platform=x64
```
- 出力: `bin\Debug\AveryDennisonUtil.exe`(Release は `bin\Release\`)。
- 補足: このマシンには vcpkg のグローバル統合が入っているが、本プロジェクトは未使用のため
`.vcxproj` で `VcpkgEnabled=false` にして無効化済み(不要な include 注入と pwsh デプロイ手順を回避)。
## 使い方
1. `用紙サイズ` ボタンでラベル寸法を設定(ロールラベルのプリセットあり。`余白` を入れると破線ガイドが出て整列の基準になる)。
2. `テキスト`/`画像`/`矩形枠`/`直線` ボタンでアイテムを追加。
3. キャンバス上でドラッグ移動、ハンドルでリサイズ。ダブルクリック(または `プロパティ` ボタン)で編集。`Del` キー / `削除` ボタンで削除。
4. コピー/複製(`コピー`+`貼付` または `複製` / `Ctrl+C,V,D`)、重なり順(`前面`/`背面`)、`整列...` で配置を整える。`グリッド/スナップ` を入れると 1mm グリッドへ吸着し、矢印キーで微調整できる(`Shift` で大きく移動)。
5. 既存ラベル等に合わせたい場合は `原紙...` で下絵を読み込む。読込時に用紙を原紙サイズへ合わせるか(埋め込みDPI / 用紙DPI)を選択。薄く表示された原紙に合わせてアイテムを配置し、原紙そのものも印刷したいときは `原紙を印刷` をチェック。
6. `印刷` ボタン → プリンタを選んで印刷。
7. `保存` / `名前を付けて保存` で `.otz` 保存、`開く` で再読込。
## 検証のヒント
実機が無い場合、`Microsoft Print to PDF` へ印刷すると 203dpi 相当の解像度差を含めて
配置・サイズが mm 通りかを PDF 上で確認できる。
+101
View File
@@ -0,0 +1,101 @@
// RenderScene.cpp
#include "pch.h"
#include "RenderScene.h"
#include "LabelLayout.h"
#include "PrintItem.h"
#include "ImageItem.h"
#pragma comment(lib, "msimg32.lib") // CDC::AlphaBlend(原紙の薄表示)
// レイアウト1枚分を dc に描画する。画面エディタと印刷の共通描画パス。
// 描画順は 用紙白地(画面のみ)→ 原紙 → グリッド/余白ガイド(画面のみ)→ 各アイテム。
// screen=true : 画面用。用紙の白地・グリッド・余白ガイド・選択枠を描く。原紙は薄く表示。
// screen=false: 印刷用。原紙は m_printTemplate のときだけ・等倍・薄表示なしで描く。
// map がDPI/ズーム/原点を吸収するので、同じコードで解像度差を正しく出力できる。
void Render::DrawLayout(CDC& dc, const CLabelLayout& layout, const CoordMap& map,
bool screen, const CPrintItem* selected, int gridHu)
{
CRect paper(0, 0, layout.m_paperWidthHu, layout.m_paperHeightHu);
CRect paperDev = map.MmToDev(paper);
if (screen)
{
CBrush white(RGB(255, 255, 255));
dc.FillRect(paperDev, &white);
}
// 原紙(背景下絵): 全アイテムの背面。画面では常に・印刷は許可時のみ。
CImageItem* tpl = layout.Template();
if (tpl && (screen || layout.m_printTemplate))
{
tpl->SetRect(paper); // 常に用紙全体へ
tpl->Draw(dc, map, /*selected=*/false);
if (screen && paperDev.Width() > 0 && paperDev.Height() > 0)
{
// 画面では原紙を薄く被せ、上に載るアイテムを見やすくする
CDC tmp;
tmp.CreateCompatibleDC(&dc);
CBitmap wb;
wb.CreateCompatibleBitmap(&dc, 1, 1);
CBitmap* pOldB = tmp.SelectObject(&wb);
tmp.SetPixelV(0, 0, RGB(255, 255, 255));
BLENDFUNCTION bf = { AC_SRC_OVER, 0, 110, 0 }; // 白を約43%重ねる
dc.AlphaBlend(paperDev.left, paperDev.top, paperDev.Width(), paperDev.Height(),
&tmp, 0, 0, 1, 1, bf);
tmp.SelectObject(pOldB);
}
}
if (screen)
{
// グリッド(アイテムの下・用紙内)。デバイス間隔が細かすぎる時は描かない。
if (gridHu > 0 && map.LenToDevX(gridHu) >= 5)
{
CPen grid(PS_SOLID, 1, RGB(225, 225, 225));
CPen* pOldG = dc.SelectObject(&grid);
for (int x = gridHu; x < layout.m_paperWidthHu; x += gridHu)
{
int dx = map.MmToDevX(x);
dc.MoveTo(dx, paperDev.top);
dc.LineTo(dx, paperDev.bottom);
}
for (int y = gridHu; y < layout.m_paperHeightHu; y += gridHu)
{
int dy = map.MmToDevY(y);
dc.MoveTo(paperDev.left, dy);
dc.LineTo(paperDev.right, dy);
}
dc.SelectObject(pOldG);
}
// 余白ガイド(破線)
if (layout.m_marginHu > 0)
{
CRect mdev = map.MmToDev(layout.ContentRect());
int oldBk = dc.SetBkMode(TRANSPARENT);
CPen mpen(PS_DOT, 1, RGB(150, 150, 200));
CPen* pOldM = dc.SelectObject(&mpen);
CBrush* pOldMB = (CBrush*)dc.SelectStockObject(NULL_BRUSH);
dc.Rectangle(mdev);
dc.SelectObject(pOldMB);
dc.SelectObject(pOldM);
dc.SetBkMode(oldBk);
}
CPen edge(PS_SOLID, 1, RGB(120, 120, 120));
CPen* pOld = dc.SelectObject(&edge);
CBrush* pOldBrush = (CBrush*)dc.SelectStockObject(NULL_BRUSH);
dc.Rectangle(paperDev);
dc.SelectObject(pOldBrush);
dc.SelectObject(pOld);
}
for (int i = 0; i < layout.GetItemCount(); ++i)
{
CPrintItem* p = layout.GetItem(i);
bool sel = screen && (p == selected);
p->Draw(dc, map, sel);
}
}
+17
View File
@@ -0,0 +1,17 @@
// RenderScene.h : レイアウト1枚分の描画(画面キャンバスと印刷で共有)
#pragma once
#include "CoordMap.h"
class CLabelLayout;
class CPrintItem;
namespace Render
{
// layout を map に従って dc へ描画する。
// screen : true=画面(用紙の白地・枠・選択ハンドルを描く) / false=印刷
// selected : 画面で選択枠を描くアイテム(印刷時は無視)
// gridHu : >0 かつ screen のとき、その間隔(1/100mm)でグリッドを描く(0=描かない)
void DrawLayout(CDC& dc, const CLabelLayout& layout, const CoordMap& map,
bool screen, const CPrintItem* selected, int gridHu = 0);
}
+60
View File
@@ -0,0 +1,60 @@
// ShapeItem.cpp
#include "pch.h"
#include "ShapeItem.h"
IMPLEMENT_SERIAL(CShapeItem, CPrintItem, 1)
// コンストラクタ。既定は矩形枠・線幅0.3mm。
CShapeItem::CShapeItem()
: m_shape(0)
, m_lineWidthHu(30) // 0.3mm
{
}
// 図形(矩形枠 or 直線)を描画する。線幅は 1/100mm 指定なので
// CoordMap で device ピクセルへ換算してから GDI ペンを作る。
void CShapeItem::Draw(CDC& dc, const CoordMap& map, bool selected)
{
CRect dr = map.MmToDev(m_rect);
int penW = map.LenToDevX(m_lineWidthHu);
if (penW < 1) penW = 1;
CPen pen(PS_SOLID, penW, RGB(0, 0, 0));
CPen* pOld = dc.SelectObject(&pen);
if (m_shape == 1)
{
// 直線: 矩形の左上→右下
dc.MoveTo(dr.left, dr.top);
dc.LineTo(dr.right, dr.bottom);
}
else
{
// 矩形枠(塗りつぶしなし)
CBrush* pOldBrush = (CBrush*)dc.SelectStockObject(NULL_BRUSH);
dc.Rectangle(dr);
dc.SelectObject(pOldBrush);
}
dc.SelectObject(pOld);
if (selected)
DrawSelection(dc, dr);
}
// 自身の複製を生成して返す。
CPrintItem* CShapeItem::Clone() const
{
return new CShapeItem(*this);
}
// 永続化。基底(矩形)に続けて図形種別と線幅を読み書きする。
void CShapeItem::Serialize(CArchive& ar)
{
CPrintItem::Serialize(ar);
if (ar.IsStoring())
ar << m_shape << m_lineWidthHu;
else
ar >> m_shape >> m_lineWidthHu;
}
+20
View File
@@ -0,0 +1,20 @@
// ShapeItem.h : 図形(矩形枠 / 直線)印刷アイテム
// 各印刷領域の区切り線・枠線に使用。
#pragma once
#include "PrintItem.h"
class CShapeItem : public CPrintItem
{
DECLARE_SERIAL(CShapeItem)
public:
CShapeItem();
int m_shape; // 0=矩形枠, 1=直線(左上→右下)
int m_lineWidthHu; // 線幅(1/100mm)
ItemType Type() const override { return m_shape == 1 ? ItemType::Line : ItemType::Rect; }
void Draw(CDC& dc, const CoordMap& map, bool selected) override;
CPrintItem* Clone() const override;
void Serialize(CArchive& ar) override;
};
+76
View File
@@ -0,0 +1,76 @@
// TextItem.cpp
#include "pch.h"
#include "TextItem.h"
IMPLEMENT_SERIAL(CTextItem, CPrintItem, 1)
// コンストラクタ。既定値(文字列・フォント・10pt・左揃え・非太字)で初期化。
CTextItem::CTextItem()
: m_text(_T("テキスト"))
, m_fontName(_T("MS ゴシック"))
, m_pointSize(10.0)
, m_align(0)
, m_bold(FALSE)
{
}
// テキストを描画する。画面・印刷の双方から同じ本関数が呼ばれ、
// CoordMap が DPI/ズームの違いを吸収するため、物理サイズ(pt)が
// どの解像度でも正しく出る。selected=true のとき選択枠も描く。
void CTextItem::Draw(CDC& dc, const CoordMap& map, bool selected)
{
CRect dr = map.MmToDev(m_rect);
// フォント高さをデバイス単位で算出(ポイント→1/100mm→デバイス)
int fontHeight = map.PointToDevHeight(m_pointSize);
if (fontHeight < 1) fontHeight = 1;
LOGFONT lf;
ZeroMemory(&lf, sizeof(lf));
lf.lfHeight = -fontHeight; // 負値 = 文字セル高さ(=文字高さ指定)
lf.lfWeight = m_bold ? FW_BOLD : FW_NORMAL;
lf.lfCharSet = DEFAULT_CHARSET;
_tcsncpy_s(lf.lfFaceName, LF_FACESIZE, m_fontName, _TRUNCATE);
CFont font;
font.CreateFontIndirect(&lf);
CFont* pOld = dc.SelectObject(&font);
int oldBk = dc.SetBkMode(TRANSPARENT);
COLORREF oldColor = dc.SetTextColor(RGB(0, 0, 0));
UINT fmt = DT_NOPREFIX | DT_WORDBREAK;
if (m_align == 1) fmt |= DT_CENTER;
else if (m_align == 2) fmt |= DT_RIGHT;
else fmt |= DT_LEFT;
CRect textRect = dr;
dc.DrawText(m_text, textRect, fmt);
dc.SetTextColor(oldColor);
dc.SetBkMode(oldBk);
dc.SelectObject(pOld);
if (selected)
DrawSelection(dc, dr);
}
// 自身の複製を生成して返す(コピー&貼付け・複製機能で使用)。
CPrintItem* CTextItem::Clone() const
{
CTextItem* p = new CTextItem(*this);
return p;
}
// 永続化。基底(矩形)に続けてテキスト固有の属性を読み書きする。
void CTextItem::Serialize(CArchive& ar)
{
CPrintItem::Serialize(ar);
if (ar.IsStoring())
{
ar << m_text << m_fontName << m_pointSize << m_align << m_bold;
}
else
{
ar >> m_text >> m_fontName >> m_pointSize >> m_align >> m_bold;
}
}
+22
View File
@@ -0,0 +1,22 @@
// TextItem.h : テキスト印刷アイテム
#pragma once
#include "PrintItem.h"
class CTextItem : public CPrintItem
{
DECLARE_SERIAL(CTextItem)
public:
CTextItem();
CString m_text; // 印刷文字列(複数行可)
CString m_fontName; // フォント名
double m_pointSize; // ポイントサイズ
int m_align; // 0=左, 1=中央, 2=右
BOOL m_bold;
ItemType Type() const override { return ItemType::Text; }
void Draw(CDC& dc, const CoordMap& map, bool selected) override;
CPrintItem* Clone() const override;
void Serialize(CArchive& ar) override;
};
+47
View File
@@ -0,0 +1,47 @@
// TextPropsDlg.cpp
#include "pch.h"
#include "AveryDennisonUtil.h"
#include "TextPropsDlg.h"
IMPLEMENT_DYNAMIC(CTextPropsDlg, CDialog)
// コンストラクタ。各プロパティを既定値で初期化。
CTextPropsDlg::CTextPropsDlg(CWnd* pParent)
: CDialog(IDD_TEXT_PROPS, pParent)
, m_text(_T(""))
, m_fontName(_T("MS ゴシック"))
, m_pointSize(10.0)
, m_align(0)
, m_bold(FALSE)
{
}
// 入力欄と各メンバ(文字列/フォント/サイズ/揃え/太字)の関連付け+サイズ範囲検証。
void CTextPropsDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT_TEXT, m_text);
DDX_Text(pDX, IDC_EDIT_FONT, m_fontName);
DDX_Text(pDX, IDC_EDIT_PTSIZE, m_pointSize);
DDV_MinMaxDouble(pDX, m_pointSize, 1.0, 500.0);
DDX_CBIndex(pDX, IDC_COMBO_ALIGN, m_align);
DDX_Check(pDX, IDC_CHECK_BOLD, m_bold);
}
// 初期化。揃えコンボに「左/中央/右」を入れ、現在の選択を反映する。
BOOL CTextPropsDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CComboBox* pAlign = (CComboBox*)GetDlgItem(IDC_COMBO_ALIGN);
if (pAlign && pAlign->GetCount() == 0)
{
pAlign->AddString(_T("左揃え"));
pAlign->AddString(_T("中央揃え"));
pAlign->AddString(_T("右揃え"));
}
if (pAlign) pAlign->SetCurSel(m_align);
return TRUE;
}
BEGIN_MESSAGE_MAP(CTextPropsDlg, CDialog)
END_MESSAGE_MAP()
+22
View File
@@ -0,0 +1,22 @@
// TextPropsDlg.h : テキストアイテムのプロパティ編集ダイアログ
#pragma once
#include "resource.h"
class CTextPropsDlg : public CDialog
{
DECLARE_DYNAMIC(CTextPropsDlg)
public:
CTextPropsDlg(CWnd* pParent = nullptr);
enum { IDD = IDD_TEXT_PROPS };
CString m_text;
CString m_fontName;
double m_pointSize;
int m_align;
BOOL m_bold;
protected:
void DoDataExchange(CDataExchange* pDX) override;
BOOL OnInitDialog() override;
DECLARE_MESSAGE_MAP()
};
+2
View File
@@ -0,0 +1,2 @@
// pch.cpp : プリコンパイル済みヘッダを生成するためのソース
#include "pch.h"
+23
View File
@@ -0,0 +1,23 @@
// pch.h : プリコンパイル済みヘッダ
// MFC ダイアログベース アプリ (AveryDennisonUtil) 共通インクルード
#pragma once
#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN // Windows ヘッダから使用頻度の低い部分を除外
#endif
#include "targetver.h"
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 一部の CString コンストラクタは明示的指定
// MFC の一部の警告を非表示
#ifndef _AFX_ALL_WARNINGS
#define _AFX_ALL_WARNINGS
#endif
#include <afxwin.h> // MFC のコアおよび標準コンポーネント
#include <afxext.h> // MFC の拡張部分(CScrollView/CFormView/ツールバー等)
#include <afxdtctl.h> // MFC の Internet Explorer 4 コモン コントロール サポート
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h> // MFC の Windows コモン コントロール サポート
#endif
+60
View File
@@ -0,0 +1,60 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// AveryDennisonUtil.rc で使用
//
#define IDR_MAINFRAME 128
#define IDD_ABOUTBOX 100
#define IDD_PAPER_SIZE 130
#define IDD_TEXT_PROPS 131
#define IDD_MAIN_DIALOG 132
// ----- ダイアログ コントロール -----
#define IDC_CANVAS 1100
#define IDC_EDIT_WIDTH 1000
#define IDC_EDIT_HEIGHT 1001
#define IDC_COMBO_PRESET 1002
#define IDC_EDIT_DPI 1003
#define IDC_EDIT_TEXT 1004
#define IDC_EDIT_FONT 1005
#define IDC_EDIT_PTSIZE 1006
#define IDC_COMBO_ALIGN 1007
#define IDC_CHECK_BOLD 1008
#define IDC_CHECK_GRID 1009
#define IDC_EDIT_MARGIN 1010
#define IDC_CHECK_PRINT_TEMPLATE 1011
// ----- コマンド -----
#define ID_INSERT_TEXT 32771
#define ID_INSERT_RECT 32772
#define ID_INSERT_LINE 32773
#define ID_INSERT_IMAGE 32774
#define ID_EDIT_PROPERTIES 32775
#define ID_EDIT_DELETE_ITEM 32776
#define ID_PAPER_SIZE 32777
#define ID_VIEW_ZOOMIN 32778
#define ID_VIEW_ZOOMOUT 32779
#define ID_ITEM_COPY 32780
#define ID_ITEM_PASTE 32781
#define ID_ITEM_DUPLICATE 32782
#define ID_ITEM_FRONT 32783
#define ID_ITEM_BACK 32784
#define ID_ALIGN_MENU 32785
// 整列サブ項目(連番。AlignSelected の how=ID-ID_ALIGN_LEFT に対応)
#define ID_ALIGN_LEFT 32786
#define ID_ALIGN_HCENTER 32787
#define ID_ALIGN_RIGHT 32788
#define ID_ALIGN_TOP 32789
#define ID_ALIGN_VCENTER 32790
#define ID_ALIGN_BOTTOM 32791
#define ID_TEMPLATE_LOAD 32792
#define ID_TEMPLATE_CLEAR 32793
// Next default values for new objects
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 132
#define _APS_NEXT_COMMAND_VALUE 32794
#define _APS_NEXT_CONTROL_VALUE 1012
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
+6
View File
@@ -0,0 +1,6 @@
#pragma once
// 最新の Windows プラットフォームを対象とする場合は SDKDDKVer.h をインクルードする。
// 以前の Windows プラットフォーム向けにビルドする場合は WinSDKVer.h と
// _WIN32_WINNT マクロを先に定義してからインクルードする。
#include <SDKDDKVer.h>