Initial commit: OtozaiPrinter (AveryDennisonUtil) ラベル/レシート レイアウト設計・印刷 MFC アプリ
- 用紙設計キャンバス(1/100mm 基準)・テキスト/画像/矩形/直線アイテム - コピー/複製/Zオーダー/整列/グリッドスナップ、原紙(下絵)読込と印刷可否 - GDI 直接印刷、.otz 保存/再読込(版数つき) - MFC/CRT 静的リンクで VS2022 IDE から F5 デバッグ可
This commit is contained in:
+14
@@ -0,0 +1,14 @@
|
||||
# エディタ/ツールのローカル設定
|
||||
.claude/
|
||||
|
||||
# ビルド成果物
|
||||
/bin/
|
||||
/obj/
|
||||
*.user
|
||||
*.aps
|
||||
*.suo
|
||||
.vs/
|
||||
ipch/
|
||||
*.pdb
|
||||
*.ilk
|
||||
*.idb
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
};
|
||||
@@ -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()
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
};
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
@@ -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
@@ -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;
|
||||
};
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
};
|
||||
@@ -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
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
// 最新の Windows プラットフォームを対象とする場合は SDKDDKVer.h をインクルードする。
|
||||
// 以前の Windows プラットフォーム向けにビルドする場合は WinSDKVer.h と
|
||||
// _WIN32_WINNT マクロを先に定義してからインクルードする。
|
||||
#include <SDKDDKVer.h>
|
||||
Reference in New Issue
Block a user