Add a 'Stamp' widget.

Place one or multiple stamps on scanned pages in defined locations.

A stamp is understood here as
 - a transparent text in a transparent frame with no borders
 - an opaque text on an opaque background with no borders.

Stamp parameters:
 - text
 - font
 - foreground colour
 - background colour
 - angle of rotation
 - transparency.

Locations:
 - centre
 - cardinal directions
 - inter-cardinal directions.
This commit is contained in:
Saleem Edah-Tally
2025-07-01 22:38:07 +02:00
parent c2c792dd3d
commit a2045aa1f6
38 changed files with 3376 additions and 71 deletions

View File

@@ -13,9 +13,13 @@ include_directories(${CMAKE_CURRENT_LIST_DIR}
Resources/InsaneWidget Resources/InsaneWidget
Resources/Utilities Resources/Utilities
Resources/InsaneWidget/UI Resources/InsaneWidget/UI
Resources/StampWidget
Resources/StampWidget/UI
Resources/UI/S7) Resources/UI/S7)
add_subdirectory(Resources/Utilities) add_subdirectory(Resources/Utilities)
add_subdirectory(Resources/StampWidget)
add_subdirectory(Resources/InsaneWidget) add_subdirectory(Resources/InsaneWidget)
add_executable(s7 add_executable(s7
@@ -25,6 +29,6 @@ add_executable(s7
install(TARGETS s7 RUNTIME DESTINATION bin) install(TARGETS s7 RUNTIME DESTINATION bin)
target_link_libraries(s7 minutils insanewidget target_link_libraries(s7 insanewidget
${wxWidgets_LIBRARIES} ${wxWidgets_LIBRARIES}
) )

View File

@@ -5,9 +5,10 @@ This is a simple scanning application with these goals:
- full page scan - full page scan
- known number of pages to scan - known number of pages to scan
- double-sided handling - double-sided handling
- multiple output file format: PNG, JPEG, TIFF, PNM and PDF. - multiple output file format: PNG, JPEG, TIFF, PNM and PDF
- apply an optional stamp on each page.
It is based on [libinsane](https://gitlab.gnome.org/World/OpenPaperwork/libinsane) and written with [wxWidgets](https://wxwidgets.org). It is based on [libinsane](https://gitlab.gnome.org/World/OpenPaperwork/libinsane) and [wxWidgets](https://wxwidgets.org).
![S7_01](S7_01.png) ![S7_01](S7_01.png)
@@ -22,6 +23,7 @@ Inputs:
- the scanner and its minimal parameters (source, mode, resolution) - the scanner and its minimal parameters (source, mode, resolution)
- page size - page size
- output file format. - output file format.
- an optional stamp text
Right click on the 'New' label to specify the number of faces and whether double-sided scanning is needed. Right click on the 'New' label to specify the number of faces and whether double-sided scanning is needed.

View File

@@ -9,23 +9,24 @@ find_package(LibInsane REQUIRED)
find_package(PoDoFo REQUIRED) find_package(PoDoFo REQUIRED)
find_package(Paper REQUIRED) find_package(Paper REQUIRED)
include_directories(${CMAKE_CURRENT_LIST_DIR} include_directories(${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/UI ${CMAKE_CURRENT_LIST_DIR}/UI
${CMAKE_CURRENT_LIST_DIR}/../StampWidget
${CMAKE_CURRENT_LIST_DIR}/../StampWidget/UI
${CMAKE_CURRENT_LIST_DIR}/../Utilities) ${CMAKE_CURRENT_LIST_DIR}/../Utilities)
add_library(insanewidget STATIC add_library(insanewidget STATIC
UI/InsaneWidget.cpp UI/InsaneWidget.cpp
UI/ScannerWidget.cpp UI/ScannerWidget.cpp
Common.h DefsInsaneWidget.h
XInsaneWidget.cpp XInsaneWidget.cpp
XScannerWidget.cpp XScannerWidget.cpp
InsaneWorker.cpp InsaneWorker.cpp
PixelToImageWriter.cpp PixelToImageWriter.cpp
PixelToPdfWriter.cpp) PixelToPdfWriter.cpp)
target_link_libraries(insanewidget minutils target_link_libraries(insanewidget minutils stampwidget
${wxWidgets_LIBRARIES} ${wxWidgets_LIBRARIES}
${LIBINSANE_LIBRARIES} ${LIBINSANE_LIBRARIES}
${PODOFO_LIBRARIES} ${PODOFO_LIBRARIES}

View File

@@ -1,14 +1,14 @@
// /* // /*
// * File: Common.h // * File: DefsInsaneWidget.h
// * Author: Saleem Edah-Tally - nmset@yandex.com // * Author: Saleem Edah-Tally - nmset@yandex.com
// * License : CeCILL-C // * License : CeCILL-C
// * Copyright Saleem Edah-Tally - © 2025 // * Copyright Saleem Edah-Tally - © 2025
// * // *
// * Created on 27 06 2025, 20:34 // * Created on 08 07 2025, 20:44
// */ // */
#ifndef COMMON_H #ifndef DEFSINSANEWIDGET_H
#define COMMON_H #define DEFSINSANEWIDGET_H
#include <string> #include <string>
#include <map> #include <map>
@@ -27,4 +27,4 @@ static void UpdateExtensionsMap()
Extensions[PNM] = "pnm"; Extensions[PNM] = "pnm";
} }
#endif // COMMON_H #endif // DEFSINSANEWIDGET_H

View File

@@ -13,7 +13,6 @@
#include <sstream> #include <sstream>
#include <fstream> #include <fstream>
#include <paper.h> #include <paper.h>
#include <libinsane/log.h>
#include <libinsane/safebet.h> #include <libinsane/safebet.h>
#include <libinsane/error.h> #include <libinsane/error.h>
#include <libinsane/util.h> #include <libinsane/util.h>
@@ -273,6 +272,7 @@ bool InsaneWorker::ConfigureDevice(const std::string& deviceId,
* Sounds weird but needed. * Sounds weird but needed.
*/ */
lis_set_option(m_sourceItem, OPT_NAME_SOURCE, source.c_str()); lis_set_option(m_sourceItem, OPT_NAME_SOURCE, source.c_str());
if (resolution > 0) // No resolution with v4l devices.
lis_set_option(m_sourceItem, OPT_NAME_RESOLUTION, to_string(resolution).c_str()); lis_set_option(m_sourceItem, OPT_NAME_RESOLUTION, to_string(resolution).c_str());
pair<double, double> br; pair<double, double> br;
if (GetBottomRight(br)) if (GetBottomRight(br))

View File

@@ -8,13 +8,16 @@
// */ // */
#include "PixelToImageWriter.h" #include "PixelToImageWriter.h"
#include <Common.h> #include <StampWorker.h>
#include <fstream> #include <fstream>
#include <DefsInsaneWidget.h>
#include <DefsStampWidget.h>
using namespace std; using namespace std;
bool PixelToImageWriter::Convert(const std::string& pixelFilePath, bool PixelToImageWriter::Convert(const std::string& pixelFilePath,
int imageWidth, int imageHeight, int imageWidth, int imageHeight,
std::vector<StampDescriptor*> * descriptors,
int outputFormat, wxImage * image) int outputFormat, wxImage * image)
{ {
UpdateExtensionsMap(); UpdateExtensionsMap();
@@ -35,6 +38,16 @@ bool PixelToImageWriter::Convert(const std::string& pixelFilePath,
raw.assign(istreambuf_iterator<char>(ifs), istreambuf_iterator<char>()); raw.assign(istreambuf_iterator<char>(ifs), istreambuf_iterator<char>());
outImage->SetData((unsigned char*) raw.data(), imageWidth, imageHeight, true); // true +++ outImage->SetData((unsigned char*) raw.data(), imageWidth, imageHeight, true); // true +++
if (descriptors)
{
for (StampDescriptor * descriptor : *descriptors)
{
if (descriptor)
{
StampWorker::StampBackground(*outImage, descriptor->image, descriptor->location);
}
}
}
switch (outputFormat) switch (outputFormat)
{ {

View File

@@ -10,19 +10,26 @@
#ifndef PIXELTOIMAGEWRITER_H #ifndef PIXELTOIMAGEWRITER_H
#define PIXELTOIMAGEWRITER_H #define PIXELTOIMAGEWRITER_H
#include "Common.h" #include "DefsInsaneWidget.h"
#include <string> #include <string>
#include <vector>
#include <wx/wx.h> #include <wx/wx.h>
struct StampDescriptor;
/** /**
* Create an image file from a raw scanned file.\n * Create an image file from a raw scanned file.\n
* Optionally,\n
* - use an wxImage object from the application\n
* - blend a stamp image on the converted image.\n
*/ */
class PixelToImageWriter class PixelToImageWriter
{ {
DECLARE_DYNAMIC_CLASS( PixelToImageWriter )
public: public:
static bool Convert(const std::string& pixelFilePath, static bool Convert(const std::string& pixelFilePath,
int imageWidth, int imageHeight, int outputFormat = PNG, int imageWidth, int imageHeight, std::vector<StampDescriptor*> * descriptors,
wxImage * image = nullptr); int outputFormat = PNG, wxImage * image = nullptr);
}; };
#endif // PIXELTOIMAGEWRITER_H #endif // PIXELTOIMAGEWRITER_H

View File

@@ -9,7 +9,11 @@
#include "PixelToPdfWriter.h" #include "PixelToPdfWriter.h"
#include <iostream> #include <iostream>
#include <sstream>
#include <memory> #include <memory>
#include <StampWorker.h>
#include <DefsInsaneWidget.h>
#include <DefsStampWidget.h>
using namespace std; using namespace std;
using namespace PoDoFo; using namespace PoDoFo;
@@ -30,6 +34,7 @@ PixelToPdfWriter::PixelToPdfWriter()
bool PixelToPdfWriter::AddPageAt(const std::string& pixelFile, uint width, uint height, uint index, bool PixelToPdfWriter::AddPageAt(const std::string& pixelFile, uint width, uint height, uint index,
std::vector<StampDescriptor*> * descriptors,
PoDoFo::PdfPageSize pageSize, PoDoFo::PdfColorSpace) PoDoFo::PdfPageSize pageSize, PoDoFo::PdfColorSpace)
{ {
try try
@@ -46,6 +51,23 @@ bool PixelToPdfWriter::AddPageAt(const std::string& pixelFile, uint width, uint
ifstream ifs(pixelFile, ios::binary); ifstream ifs(pixelFile, ios::binary);
string content; string content;
content.assign(istreambuf_iterator<char>(ifs), istreambuf_iterator<char>()); content.assign(istreambuf_iterator<char>(ifs), istreambuf_iterator<char>());
if (descriptors)
{
for (StampDescriptor * descriptor : *descriptors)
{
if (!descriptor)
continue;
wxImage background;
background.SetData(reinterpret_cast<unsigned char*> (content.data()), width, height, true);
StampWorker::StampBackground(background, descriptor->image, descriptor->location);
stringstream ssStamped;
ssStamped.write((const char*) (background.GetData()), width * height * 3);
ssStamped.flush();
content.clear();
content = ssStamped.str();
}
}
bufferview bv(content); bufferview bv(content);
const uint pageNumber = m_doc.GetPages().GetCount(); const uint pageNumber = m_doc.GetPages().GetCount();

View File

@@ -12,13 +12,18 @@
#include <string> #include <string>
#include <map> #include <map>
#include <vector>
#include <podofo/podofo.h> #include <podofo/podofo.h>
#include <wx/wx.h>
struct StampDescriptor;
/** /**
* Create a PDF document, append or insert pages from raw scanned files.\n * Create a PDF document, append or insert pages from raw scanned files.\n
* Each image is a full page scan; it is scaled in the PDF document to the full * Each image is a full page scan; it is scaled in the PDF document to the full
* page dimensions.\n * page dimensions.\n
* Account for page size. * Account for page size.\n
* Optionally, a stamp image may be blended on the page.
*/ */
class PixelToPdfWriter class PixelToPdfWriter
{ {
@@ -27,6 +32,7 @@ public:
PixelToPdfWriter(); PixelToPdfWriter();
bool AddPageAt(const std::string& pixelFile, uint width, uint height, uint index, bool AddPageAt(const std::string& pixelFile, uint width, uint height, uint index,
std::vector<StampDescriptor*> * descriptors,
PoDoFo::PdfPageSize pageSize = PoDoFo::PdfPageSize::A4, PoDoFo::PdfPageSize pageSize = PoDoFo::PdfPageSize::A4,
PoDoFo::PdfColorSpace = PoDoFo::PdfColorSpace::DeviceRGB /*Unused*/); PoDoFo::PdfColorSpace = PoDoFo::PdfColorSpace::DeviceRGB /*Unused*/);
void Save(const std::string& pdfFile); void Save(const std::string& pdfFile);

View File

@@ -103,6 +103,7 @@ InsaneWidget::~InsaneWidget()
void InsaneWidget::Init() void InsaneWidget::Init()
{ {
////@begin InsaneWidget member initialisation ////@begin InsaneWidget member initialisation
szInsaneWidgetMain = NULL;
lblNewDoc = NULL; lblNewDoc = NULL;
txtNewDoc = NULL; txtNewDoc = NULL;
btnScan = NULL; btnScan = NULL;
@@ -119,11 +120,11 @@ void InsaneWidget::CreateControls()
////@begin InsaneWidget content construction ////@begin InsaneWidget content construction
InsaneWidget* itemPanel1 = this; InsaneWidget* itemPanel1 = this;
wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL); szInsaneWidgetMain = new wxBoxSizer(wxVERTICAL);
itemPanel1->SetSizer(itemBoxSizer2); itemPanel1->SetSizer(szInsaneWidgetMain);
wxBoxSizer* itemBoxSizer1 = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* itemBoxSizer1 = new wxBoxSizer(wxHORIZONTAL);
itemBoxSizer2->Add(itemBoxSizer1, 0, wxGROW|wxALL, 5); szInsaneWidgetMain->Add(itemBoxSizer1, 0, wxGROW|wxALL, 5);
lblNewDoc = new wxStaticText( itemPanel1, ID_NewDoc_LBL, _("New"), wxDefaultPosition, wxDefaultSize, 0 ); lblNewDoc = new wxStaticText( itemPanel1, ID_NewDoc_LBL, _("New"), wxDefaultPosition, wxDefaultSize, 0 );
if (InsaneWidget::ShowToolTips()) if (InsaneWidget::ShowToolTips())
@@ -137,7 +138,7 @@ void InsaneWidget::CreateControls()
btnScan = new wxButton( itemPanel1, ID_Scan_BTN, _("Scan"), wxDefaultPosition, wxDefaultSize, 0 ); btnScan = new wxButton( itemPanel1, ID_Scan_BTN, _("Scan"), wxDefaultPosition, wxDefaultSize, 0 );
if (InsaneWidget::ShowToolTips()) if (InsaneWidget::ShowToolTips())
btnScan->SetToolTip(_("'Left click' to start the scan project.\n'Right click' to show the scanner widget.")); btnScan->SetToolTip(_("'Left click' to start the scan project.\n'Right click' to show the scanner widget.\n'Ctrl + Right click' to show the Stamp dialog."));
itemBoxSizer1->Add(btnScan, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); itemBoxSizer1->Add(btnScan, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);
////@end InsaneWidget content construction ////@end InsaneWidget content construction

View File

@@ -25,6 +25,7 @@
*/ */
////@begin forward declarations ////@begin forward declarations
class wxBoxSizer;
////@end forward declarations ////@end forward declarations
/*! /*!
@@ -39,7 +40,7 @@
#define SYMBOL_INSANEWIDGET_STYLE wxTAB_TRAVERSAL #define SYMBOL_INSANEWIDGET_STYLE wxTAB_TRAVERSAL
#define SYMBOL_INSANEWIDGET_TITLE _("InsaneWidget") #define SYMBOL_INSANEWIDGET_TITLE _("InsaneWidget")
#define SYMBOL_INSANEWIDGET_IDNAME ID_INSANEWIDGET #define SYMBOL_INSANEWIDGET_IDNAME ID_INSANEWIDGET
#define SYMBOL_INSANEWIDGET_SIZE wxSize(400, 300) #define SYMBOL_INSANEWIDGET_SIZE wxDefaultSize
#define SYMBOL_INSANEWIDGET_POSITION wxDefaultPosition #define SYMBOL_INSANEWIDGET_POSITION wxDefaultPosition
////@end control identifiers ////@end control identifiers
@@ -87,6 +88,7 @@ public:
static bool ShowToolTips(); static bool ShowToolTips();
////@begin InsaneWidget member variables ////@begin InsaneWidget member variables
wxBoxSizer* szInsaneWidgetMain;
wxStaticText* lblNewDoc; wxStaticText* lblNewDoc;
wxTextCtrl* txtNewDoc; wxTextCtrl* txtNewDoc;
wxButton* btnScan; wxButton* btnScan;

View File

@@ -245,7 +245,7 @@
<string name="proxy-Font">""</string> <string name="proxy-Font">""</string>
<string name="proxy-Foreground colour">""</string> <string name="proxy-Foreground colour">""</string>
<string name="proxy-Header filename">"InsaneWidget.h"</string> <string name="proxy-Header filename">"InsaneWidget.h"</string>
<long name="proxy-Height">300</long> <long name="proxy-Height">-1</long>
<string name="proxy-Help text">""</string> <string name="proxy-Help text">""</string>
<bool name="proxy-Hidden">0</bool> <bool name="proxy-Hidden">0</bool>
<string name="proxy-Icon">""</string> <string name="proxy-Icon">""</string>
@@ -257,7 +257,7 @@
<string name="proxy-Texture style">"Tiled"</string> <string name="proxy-Texture style">"Tiled"</string>
<string name="proxy-Title">"InsaneWidget"</string> <string name="proxy-Title">"InsaneWidget"</string>
<string name="proxy-Tooltip text">""</string> <string name="proxy-Tooltip text">""</string>
<long name="proxy-Width">400</long> <long name="proxy-Width">-1</long>
<string name="proxy-Window kind">"wxPanel"</string> <string name="proxy-Window kind">"wxPanel"</string>
<bool name="proxy-wxBORDER_THEME">0</bool> <bool name="proxy-wxBORDER_THEME">0</bool>
<bool name="proxy-wxCAPTION">0</bool> <bool name="proxy-wxCAPTION">0</bool>
@@ -303,13 +303,21 @@
<long name="is-transient">0</long> <long name="is-transient">0</long>
<long name="locked">0</long> <long name="locked">0</long>
<long name="owns-file">1</long> <long name="owns-file">1</long>
<string name="proxy-Member variable name">""</string> <string name="proxy-AlignH">"Centre"</string>
<string name="proxy-AlignV">"Centre"</string>
<long name="proxy-Border">5</long>
<string name="proxy-Member variable name">"szInsaneWidgetMain"</string>
<string name="proxy-Orientation">"Vertical"</string> <string name="proxy-Orientation">"Vertical"</string>
<string name="proxy-Platform">"&lt;Any platform&gt;"</string> <string name="proxy-Platform">"&lt;Any platform&gt;"</string>
<long name="proxy-Stretch factor">0</long>
<bool name="proxy-wxADJUST_MINSIZE">0</bool> <bool name="proxy-wxADJUST_MINSIZE">0</bool>
<bool name="proxy-wxBOTTOM">1</bool>
<bool name="proxy-wxFIXED_MINSIZE">0</bool> <bool name="proxy-wxFIXED_MINSIZE">0</bool>
<bool name="proxy-wxLEFT">1</bool>
<bool name="proxy-wxRESERVE_SPACE_EVEN_IF_HIDDEN">0</bool> <bool name="proxy-wxRESERVE_SPACE_EVEN_IF_HIDDEN">0</bool>
<bool name="proxy-wxRIGHT">1</bool>
<bool name="proxy-wxSHAPED">0</bool> <bool name="proxy-wxSHAPED">0</bool>
<bool name="proxy-wxTOP">1</bool>
<string name="title">"wxBoxSizer V"</string> <string name="title">"wxBoxSizer V"</string>
<long name="title-mode">0</long> <long name="title-mode">0</long>
<string name="type">"dialog-control-document"</string> <string name="type">"dialog-control-document"</string>
@@ -538,7 +546,8 @@
<bool name="proxy-Separate files">0</bool> <bool name="proxy-Separate files">0</bool>
<long name="proxy-Stretch factor">0</long> <long name="proxy-Stretch factor">0</long>
<string name="proxy-Tooltip text">"'Left click' to start the scan project. <string name="proxy-Tooltip text">"'Left click' to start the scan project.
'Right click' to show the scanner widget."</string> 'Right click' to show the scanner widget.
'Ctrl + Right click' to show the Stamp dialog."</string>
<long name="proxy-Width">-1</long> <long name="proxy-Width">-1</long>
<bool name="proxy-wxADJUST_MINSIZE">0</bool> <bool name="proxy-wxADJUST_MINSIZE">0</bool>
<bool name="proxy-wxBOTTOM">1</bool> <bool name="proxy-wxBOTTOM">1</bool>
@@ -654,13 +663,21 @@
<long name="is-transient">0</long> <long name="is-transient">0</long>
<long name="locked">0</long> <long name="locked">0</long>
<long name="owns-file">1</long> <long name="owns-file">1</long>
<string name="proxy-AlignH">"Centre"</string>
<string name="proxy-AlignV">"Centre"</string>
<long name="proxy-Border">5</long>
<string name="proxy-Member variable name">""</string> <string name="proxy-Member variable name">""</string>
<string name="proxy-Orientation">"Vertical"</string> <string name="proxy-Orientation">"Vertical"</string>
<string name="proxy-Platform">"&lt;Any platform&gt;"</string> <string name="proxy-Platform">"&lt;Any platform&gt;"</string>
<long name="proxy-Stretch factor">0</long>
<bool name="proxy-wxADJUST_MINSIZE">0</bool> <bool name="proxy-wxADJUST_MINSIZE">0</bool>
<bool name="proxy-wxBOTTOM">1</bool>
<bool name="proxy-wxFIXED_MINSIZE">0</bool> <bool name="proxy-wxFIXED_MINSIZE">0</bool>
<bool name="proxy-wxLEFT">1</bool>
<bool name="proxy-wxRESERVE_SPACE_EVEN_IF_HIDDEN">0</bool> <bool name="proxy-wxRESERVE_SPACE_EVEN_IF_HIDDEN">0</bool>
<bool name="proxy-wxRIGHT">1</bool>
<bool name="proxy-wxSHAPED">0</bool> <bool name="proxy-wxSHAPED">0</bool>
<bool name="proxy-wxTOP">1</bool>
<string name="title">"wxBoxSizer V"</string> <string name="title">"wxBoxSizer V"</string>
<long name="title-mode">0</long> <long name="title-mode">0</long>
<string name="type">"dialog-control-document"</string> <string name="type">"dialog-control-document"</string>

View File

@@ -12,7 +12,10 @@
#include <MiscTools.h> #include <MiscTools.h>
#include "PixelToImageWriter.h" #include "PixelToImageWriter.h"
#include "PixelToPdfWriter.h" #include "PixelToPdfWriter.h"
#include <Common.h> #include <DefsInsaneWidget.h>
#include <DefsStampWidget.h>
#include <XStampWidget.h>
#include <StampWorker.h>
using namespace std; using namespace std;
@@ -46,13 +49,16 @@ public:
{ {
} }
/* /*
* mode, outputType, adf, doubleSided, total, PaperSize: * mode, resolution, outputType, adf, doubleSided, total, PaperSize:
* These are not updated here if there are pending jobs. * These are not updated here if there are pending jobs.
* However, * However,
* deviceId, source, sourceIndex, mode, resolution, paperSize * deviceId, source, sourceIndex, mode, resolution, paperSize
* can be changed if there are pending jobs in ConfigureDevice() since it is * can be changed if there are pending jobs in ConfigureDevice() since it is
* called before these setters. They won't change on their own but by user * called before these setters. They won't change on their own but by user
* interaction. * interaction.
* The 'resolution' variable is used downstream to distinguish between v4l
* and real scanners. v4l devices do not have a source and a resolution
* tunables.
*/ */
void SetMode(const string& mode) void SetMode(const string& mode)
{ {
@@ -60,6 +66,12 @@ public:
return; return;
m_mode = mode; m_mode = mode;
} }
void SetResolution(int resolution)
{
if (m_pixelFiles.size())
return;
m_resolution = resolution;
}
void SetOutputType(int outputType) void SetOutputType(int outputType)
{ {
if (m_pixelFiles.size()) if (m_pixelFiles.size())
@@ -101,6 +113,10 @@ public:
return; return;
m_paperSize = paperSize; m_paperSize = paperSize;
} }
void SetStampDescriptors(vector<StampDescriptor*> * descriptors)
{
m_stampDescriptors = descriptors;
}
std::pair<int, int> GetStartAndIncrement(InsaneWorker * insaneWorker) std::pair<int, int> GetStartAndIncrement(InsaneWorker * insaneWorker)
{ {
wxASSERT_MSG(insaneWorker != nullptr, "insaneWorker is NULL."); wxASSERT_MSG(insaneWorker != nullptr, "insaneWorker is NULL.");
@@ -166,7 +182,8 @@ public:
if (m_outputType != PDF) if (m_outputType != PDF)
{ {
// Convert pixel file to PNG using netpbm. // Convert pixel file to PNG using netpbm.
if (!PixelToImageWriter::Convert(filePath, imageAttributes.width, imageAttributes.height, m_outputType)) if (!PixelToImageWriter::Convert(filePath, imageAttributes.width, imageAttributes.height,
m_stampDescriptors, m_outputType))
{ {
const wxString msg = _("Failed to create output image."); const wxString msg = _("Failed to create output image.");
cerr << msg << endl; cerr << msg << endl;
@@ -191,7 +208,8 @@ public:
cerr << msg << endl; cerr << msg << endl;
pageSize = PoDoFo::PdfPageSize::A4; pageSize = PoDoFo::PdfPageSize::A4;
} }
if (!m_pixelToPdfWriter->AddPageAt(filePath, imageAttributes.width, imageAttributes.height, index, pageSize)) if (!m_pixelToPdfWriter->AddPageAt(filePath, imageAttributes.width, imageAttributes.height, index,
m_stampDescriptors, pageSize))
{ {
const wxString msg = _("Failed to add page to PDF document."); const wxString msg = _("Failed to add page to PDF document.");
cerr << msg << endl; cerr << msg << endl;
@@ -241,6 +259,17 @@ public:
} }
} }
} }
void OnStartScanSession(uint pageIndex, const ImageAttributes& imageAttributes) override
{
if (!m_stampDescriptors)
return;
for (StampDescriptor * descriptor : *m_stampDescriptors)
{
if (descriptor->text.IsEmpty())
continue;
descriptor->image = StampWorker::CreateStamp(descriptor, m_resolution);
}
}
void Reset() void Reset()
{ {
// Don't reset calculated variables that depend on widgets. // Don't reset calculated variables that depend on widgets.
@@ -255,7 +284,9 @@ private:
wxWeakRef<TimeredStatusBar> m_sb = nullptr; wxWeakRef<TimeredStatusBar> m_sb = nullptr;
std::unique_ptr<PixelToPdfWriter> m_pixelToPdfWriter; std::unique_ptr<PixelToPdfWriter> m_pixelToPdfWriter;
PixelFilesMap m_pixelFiles; PixelFilesMap m_pixelFiles;
vector<StampDescriptor*> * m_stampDescriptors = nullptr;
string m_mode = "Color"; string m_mode = "Color";
int m_resolution = -1;
uint m_outputType = PDF; uint m_outputType = PDF;
wxString m_paperSize = _T("A4"); wxString m_paperSize = _T("A4");
bool m_adf = false; bool m_adf = false;
@@ -268,12 +299,20 @@ private:
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
IMPLEMENT_CLASS( XInsaneWidget, InsaneWidget )
XInsaneWidget::XInsaneWidget(wxWindow* parent, TimeredStatusBar * sb, wxConfig * config, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) XInsaneWidget::XInsaneWidget(wxWindow* parent, TimeredStatusBar * sb, wxConfig * config, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
: InsaneWidget(parent, id, pos, size, style) : InsaneWidget(parent, id, pos, size, style)
{ {
UpdateExtensionsMap(); UpdateExtensionsMap();
m_config = config; m_config = config;
m_sb = sb; m_sb = sb;
}
XInsaneWidget::~XInsaneWidget() = default; // Important for mixing unique_ptr and PIMPL.
void XInsaneWidget::Setup()
{
lblNewDoc->Bind ( wxEVT_RIGHT_UP, &XInsaneWidget::OnLblNewDocRightClick, this ); lblNewDoc->Bind ( wxEVT_RIGHT_UP, &XInsaneWidget::OnLblNewDocRightClick, this );
txtNewDoc->Bind ( wxEVT_KEY_UP, &XInsaneWidget::OnTxtNewDocKeyPressed, this ); txtNewDoc->Bind ( wxEVT_KEY_UP, &XInsaneWidget::OnTxtNewDocKeyPressed, this );
@@ -293,7 +332,6 @@ XInsaneWidget::XInsaneWidget(wxWindow* parent, TimeredStatusBar * sb, wxConfig *
backgroundDiscovery->Run(); backgroundDiscovery->Run();
} }
XInsaneWidget::~XInsaneWidget() = default; // Important for mixing unique_ptr and PIMPL.
// Show a popup to specifiy the number of pages to scan and back-sided scanning. // Show a popup to specifiy the number of pages to scan and back-sided scanning.
void XInsaneWidget::OnLblNewDocRightClick ( wxMouseEvent& evt ) void XInsaneWidget::OnLblNewDocRightClick ( wxMouseEvent& evt )
@@ -328,16 +366,38 @@ void XInsaneWidget::OnTxtNewDocKeyPressed ( wxKeyEvent& evt )
evt.Skip(); evt.Skip();
} }
// Show the scanner widget. // Show the scanner widget or the stamp widgets.
void XInsaneWidget::OnBtnScanRightClick ( wxMouseEvent& evt ) void XInsaneWidget::OnBtnScanRightClick ( wxMouseEvent& evt )
{ {
if (evt.ControlDown())
{
if (!m_dlgStampWidgets)
{
m_dlgStampWidgets = std::make_unique<wxDialog>
(nullptr, wxID_ANY, _("Stamps"),
wxDefaultPosition, wxDefaultSize,
wxCAPTION | wxRESIZE_BORDER | wxCLOSE_BOX);
m_dlgStampWidgets->SetSize(600, 500);
m_dlgStampWidgets->SetSizer(new wxBoxSizer(wxVERTICAL));
m_dlgStampWidgets->Show ( false );
m_stampWidgets = std::make_unique<XStampWidgets> ( m_dlgStampWidgets.get());
m_stampWidgets->Setup(m_config);
m_dlgStampWidgets->GetSizer()->Add(m_stampWidgets.get(), 1, wxGROW | wxALL, 5);
}
m_dlgStampWidgets->Show();
}
else
{
if ( m_scannerWidget->cmbDevices->GetCount() ) if ( m_scannerWidget->cmbDevices->GetCount() )
{ {
const wxSize current = m_scannerWidget->GetSize(); const wxSize current = m_scannerWidget->GetSize();
m_scannerWidget->SetSize ( wxSize ( 500, current.GetHeight() ) ); m_scannerWidget->SetSize ( wxSize ( 500, current.GetHeight() ) );
} }
MiscTools::ShowTransientPopup ( m_ptwScannerWidget.get(), m_scannerWidget.get() ); MiscTools::ShowTransientPopup ( m_ptwScannerWidget.get(), m_scannerWidget.get() );
evt.Skip(); }
// +++ Prevent OnLblNewDocRightClick() from being called.
// Why is OnBtnScanRightClick() not called the other way round?
evt.Skip(false);
} }
// Start scanning. // Start scanning.
@@ -367,7 +427,7 @@ void XInsaneWidget::OnBtnScanClick ( wxMouseEvent& evt )
const std::pair<int, wxString> sourceAttributes = m_scannerWidget->GetScannerSource(); const std::pair<int, wxString> sourceAttributes = m_scannerWidget->GetScannerSource();
const string source = sourceAttributes.second.ToStdString(); const string source = sourceAttributes.second.ToStdString();
const string mode = m_scannerWidget->GetScannerMode().ToStdString(); const string mode = m_scannerWidget->GetScannerMode().ToStdString();
int resolution = 300; int resolution = -1; // No resolution with v4l devices.
if ( !m_scannerWidget->GetScannerResolution().IsEmpty() ) if ( !m_scannerWidget->GetScannerResolution().IsEmpty() )
{ {
resolution = std::stoi ( m_scannerWidget->GetScannerResolution().ToStdString() ); resolution = std::stoi ( m_scannerWidget->GetScannerResolution().ToStdString() );
@@ -379,10 +439,13 @@ void XInsaneWidget::OnBtnScanClick ( wxMouseEvent& evt )
{ {
m_scanProject->SetADF(adf); m_scanProject->SetADF(adf);
m_scanProject->SetMode(mode); m_scanProject->SetMode(mode);
m_scanProject->SetResolution(resolution);
m_scanProject->SetOutputType(outputType); m_scanProject->SetOutputType(outputType);
m_scanProject->SetPaperSize(paperSize); m_scanProject->SetPaperSize(paperSize);
m_scanProject->SetDoubleSided(doubleSided); m_scanProject->SetDoubleSided(doubleSided);
m_scanProject->SetTotalNumberOfSides(total); m_scanProject->SetTotalNumberOfSides(total);
m_stampDescriptors = m_stampWidgets->GetStampDescriptors();
m_scanProject->SetStampDescriptors(m_stampDescriptors);
std::pair<int, int> startAndIncrement = m_scanProject->GetStartAndIncrement(m_insaneWorker.get()); std::pair<int, int> startAndIncrement = m_scanProject->GetStartAndIncrement(m_insaneWorker.get());

View File

@@ -16,18 +16,23 @@
#include <InsaneWidget.h> #include <InsaneWidget.h>
#include <InsaneWorker.h> #include <InsaneWorker.h>
#include <XScannerWidget.h> #include <XScannerWidget.h>
#include <XStampWidget.h>
#include <XStampWidgets.h>
#include <TimeredStatusBar.h> #include <TimeredStatusBar.h>
#include <ConfigEditorPopup.h> #include <ConfigEditorPopup.h>
#include <memory> #include <memory>
#include <vector>
class BackgroundScannerDiscoveryEVH; class BackgroundScannerDiscoveryEVH;
class ScanProjectHandler; // An event handler extending InsaneWorkerEvent. class ScanProjectHandler; // An event handler extending InsaneWorkerEvent.
class XStampWidget;
struct StampDescriptor;
// Page index (not page number), {pixel file path, {pixel count, image width, image height}}. // Page index (not page number), {pixel file path, {pixel count, image width, image height}}.
typedef std::map<uint, std::tuple<std::string, InsaneWorkerEvent::ImageAttributes>> PixelFilesMap; typedef std::map<uint, std::tuple<std::string, InsaneWorkerEvent::ImageAttributes>> PixelFilesMap;
/** /**
* Shows a label, a disabled text box and a button. * Shows a label, a disabled text box, and a button.
* *
* Label: * Label:
* - Right click: define the number of pages to scan and double-sided scanning. * - Right click: define the number of pages to scan and double-sided scanning.
@@ -48,9 +53,11 @@ typedef std::map<uint, std::tuple<std::string, InsaneWorkerEvent::ImageAttribute
* Button: * Button:
* - Right click: shows the scanner widget. * - Right click: shows the scanner widget.
* - Left click: starts scanning. * - Left click: starts scanning.
* - Ctrl + Right click: show the stamp widgets dialog.
*/ */
class XInsaneWidget : public InsaneWidget class XInsaneWidget : public InsaneWidget
{ {
DECLARE_DYNAMIC_CLASS( XInsaneWidget )
public: public:
virtual ~XInsaneWidget(); virtual ~XInsaneWidget();
XInsaneWidget( wxWindow* parent, TimeredStatusBar * sb, wxConfig * config, wxWindowID id = SYMBOL_INSANEWIDGET_IDNAME, const wxPoint& pos = SYMBOL_INSANEWIDGET_POSITION, const wxSize& size = SYMBOL_INSANEWIDGET_SIZE, long style = SYMBOL_INSANEWIDGET_STYLE ); XInsaneWidget( wxWindow* parent, TimeredStatusBar * sb, wxConfig * config, wxWindowID id = SYMBOL_INSANEWIDGET_IDNAME, const wxPoint& pos = SYMBOL_INSANEWIDGET_POSITION, const wxSize& size = SYMBOL_INSANEWIDGET_SIZE, long style = SYMBOL_INSANEWIDGET_STYLE );
@@ -58,14 +65,19 @@ public:
void ResetScanProject(); void ResetScanProject();
void CancelScanning(); // Not tested, probably doesn't work as intended. void CancelScanning(); // Not tested, probably doesn't work as intended.
void EnableScanButton(bool enable); // For CallAfter. void EnableScanButton(bool enable); // For CallAfter.
void Setup();
private: private:
wxConfig * m_config; wxConfig * m_config;
wxWeakRef<TimeredStatusBar> m_sb; wxWeakRef<TimeredStatusBar> m_sb;
std::vector<StampDescriptor*> * m_stampDescriptors;
// Contains a popup to define the number of pages and double-sided scanning. // Contains a popup to define the number of pages and double-sided scanning.
std::unique_ptr<ConfigEditorPopup> m_pageStack; std::unique_ptr<ConfigEditorPopup> m_pageStack;
// Contains the scanner widget. // Contains the scanner widget.
std::unique_ptr<wxPopupTransientWindow> m_ptwScannerWidget; std::unique_ptr<wxPopupTransientWindow> m_ptwScannerWidget;
// Contains the stamp widgets.
std::unique_ptr<wxDialog> m_dlgStampWidgets;
std::unique_ptr<XStampWidgets> m_stampWidgets;
// Available devices and minimal options. // Available devices and minimal options.
std::unique_ptr<XScannerWidget> m_scannerWidget; std::unique_ptr<XScannerWidget> m_scannerWidget;
std::unique_ptr<InsaneWorker> m_insaneWorker; std::unique_ptr<InsaneWorker> m_insaneWorker;

View File

@@ -9,13 +9,15 @@
#include "XScannerWidget.h" #include "XScannerWidget.h"
#include "XClientData.hpp" #include "XClientData.hpp"
#include "Common.h" #include "DefsInsaneWidget.h"
#include <libinsane/constants.h> #include <libinsane/constants.h>
#include <fstream> #include <fstream>
#include <vector> #include <vector>
using namespace std; using namespace std;
IMPLEMENT_CLASS( XScannerWidget, ScannerWidget )
XScannerWidget::~XScannerWidget() XScannerWidget::~XScannerWidget()
{} {}

View File

@@ -16,7 +16,6 @@
#include <ScannerWidget.h> #include <ScannerWidget.h>
#include "TimeredStatusBar.h" #include "TimeredStatusBar.h"
#include "InsaneWorker.h" #include "InsaneWorker.h"
#include "Common.h"
#include <map> #include <map>
class BackgroundScannerDiscoveryEvent; class BackgroundScannerDiscoveryEvent;
@@ -33,6 +32,7 @@ class BackgroundScannerDiscoveryEvent;
class XScannerWidget : public ScannerWidget class XScannerWidget : public ScannerWidget
{ {
DECLARE_DYNAMIC_CLASS( XScannerWidget )
friend class BackgroundScannerDiscovery; friend class BackgroundScannerDiscovery;
public: public:
XScannerWidget() {}; XScannerWidget() {};

View File

@@ -6,8 +6,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-06-26 22:47+0200\n" "POT-Creation-Date: 2025-07-09 16:00+0200\n"
"PO-Revision-Date: 2025-06-26 22:47+0200\n" "PO-Revision-Date: 2025-07-09 16:03+0200\n"
"Last-Translator: Saleem EDAH-TALLY <set@nmset.info>\n" "Last-Translator: Saleem EDAH-TALLY <set@nmset.info>\n"
"Language-Team: French <kde-francophone@kde.org>\n" "Language-Team: French <kde-francophone@kde.org>\n"
"Language: fr_FR\n" "Language: fr_FR\n"
@@ -15,7 +15,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Lokalize 25.04.2\n" "X-Generator: Lokalize 25.04.3\n"
#: ../../Resources/UI/S7/s7.cpp:126 ../..//Resources/UI/S7/s7.cpp:126 #: ../../Resources/UI/S7/s7.cpp:126 ../..//Resources/UI/S7/s7.cpp:126
msgid "" msgid ""
@@ -40,14 +40,23 @@ msgstr ""
"'CTRL + clic' : à propos" "'CTRL + clic' : à propos"
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:128 #: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:128
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:129
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:132
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:125
msgid "New" msgid "New"
msgstr "Nouveau" msgstr "Nouveau"
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:130 #: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:130
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:131
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:134
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:127
msgid "'Right' click to define a scan project." msgid "'Right' click to define a scan project."
msgstr "Clic droit pour définir un projet de numérisation" msgstr "Clic droit pour définir un projet de numérisation"
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:135 #: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:135
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:136
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:139
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:132
msgid "" msgid ""
"Full path to destination file without the extension; it is determined by the " "Full path to destination file without the extension; it is determined by the "
"output type." "output type."
@@ -56,10 +65,15 @@ msgstr ""
"déterminée par le format de sortie." "déterminée par le format de sortie."
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:138 #: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:138
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:139
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:142
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:135
msgid "Scan" msgid "Scan"
msgstr "Numériser" msgstr "Numériser"
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:140 #: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:140
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:141
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:144
msgid "" msgid ""
"'Left click' to start the scan project.\n" "'Left click' to start the scan project.\n"
"'Right click' to show the scanner widget." "'Right click' to show the scanner widget."
@@ -93,41 +107,57 @@ msgstr "Résolution de la numérisation"
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:128 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:128
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:107 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:107
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:132
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:133
msgid "A scan library error occurred." msgid "A scan library error occurred."
msgstr "Une erreur de bibliothèque est survenue." msgstr "Une erreur de bibliothèque est survenue."
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:134 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:134
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:113 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:113
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:138
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:139
msgid "A general error occurred." msgid "A general error occurred."
msgstr "Une erreur générale est survenue." msgstr "Une erreur générale est survenue."
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:138 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:138
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:117 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:117
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:142
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:143
msgid "A session read error occurred." msgid "A session read error occurred."
msgstr "Une erreur de lecture est survenue." msgstr "Une erreur de lecture est survenue."
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:145 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:145
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:124 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:124
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:149
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:150
msgid "Session cancelled." msgid "Session cancelled."
msgstr "Session annulée." msgstr "Session annulée."
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:162 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:162
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:141 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:141
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:166
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:167
msgid "Scanning: " msgid "Scanning: "
msgstr "Numérisation :" msgstr "Numérisation :"
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:164 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:164
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:143 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:143
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:168
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:169
msgid "Front face: " msgid "Front face: "
msgstr "Recto :" msgstr "Recto :"
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:166 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:166
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:145 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:145
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:170
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:171
msgid "Back face: " msgid "Back face: "
msgstr "Verso :" msgstr "Verso :"
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:170 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:170
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:149 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:149
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:174
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:175
msgid ". Turn the whole stack of pages." msgid ". Turn the whole stack of pages."
msgstr ". Retournez toute la pile de pages." msgstr ". Retournez toute la pile de pages."
@@ -138,16 +168,23 @@ msgstr "Échec de création d'image PNG."
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:201 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:201
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:180 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:180
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:206
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:207
msgid "Wrong paper size: " msgid "Wrong paper size: "
msgstr "Mauvaise taille de papier :" msgstr "Mauvaise taille de papier :"
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:201 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:201
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:180 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:180
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:206
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:207
msgid "; using A4." msgid "; using A4."
msgstr "; utilisation du format A4." msgstr "; utilisation du format A4."
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:207 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:207
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:186 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:186
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:212
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:213
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:214
msgid "Failed to add page to PDF document." msgid "Failed to add page to PDF document."
msgstr "Échec d'ajout de page au document PDF." msgstr "Échec d'ajout de page au document PDF."
@@ -160,17 +197,28 @@ msgstr "Format de fichier de sortie non pris en charge."
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:264 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:264
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:243 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:243
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:255
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:256
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:257
msgid "Finished." msgid "Finished."
msgstr "Terminé." msgstr "Terminé."
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:333 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:333
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:317 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:317
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:356
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:355
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:351
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:352
msgid "Scan all front faces first, then all back faces in reverse order." msgid "Scan all front faces first, then all back faces in reverse order."
msgstr "" msgstr ""
"Numériser tous les faces recto, puis toutes les faces verso en ordre inverse." "Numériser tous les faces recto, puis toutes les faces verso en ordre inverse."
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:336 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:336
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:320 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:320
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:359
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:358
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:354
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:355
msgid "Total number of sides to scan (not total number of sheets)." msgid "Total number of sides to scan (not total number of sheets)."
msgstr "" msgstr ""
"Nombre total de faces à numériser (et non pas le nombre total de feuilles)." "Nombre total de faces à numériser (et non pas le nombre total de feuilles)."
@@ -188,10 +236,14 @@ msgid "CAPS"
msgstr "CAPS" msgstr "CAPS"
#: ../../Resources/InsaneWidget/XScannerWidget.cpp:50 #: ../../Resources/InsaneWidget/XScannerWidget.cpp:50
#: ../../Resources/InsaneWidget/XScannerWidget.cpp:62
#: ../../Resources/InsaneWidget/XScannerWidget.cpp:64
msgid "Searching for devices..." msgid "Searching for devices..."
msgstr "Recherche de périphériques..." msgstr "Recherche de périphériques..."
#: ../../Resources/InsaneWidget/XScannerWidget.cpp:53 #: ../../Resources/InsaneWidget/XScannerWidget.cpp:53
#: ../../Resources/InsaneWidget/XScannerWidget.cpp:65
#: ../../Resources/InsaneWidget/XScannerWidget.cpp:67
msgid "Could not initialise insane api." msgid "Could not initialise insane api."
msgstr "Échec d'initialisation de la bibliothèque 'insane'." msgstr "Échec d'initialisation de la bibliothèque 'insane'."
@@ -199,23 +251,23 @@ msgstr "Échec d'initialisation de la bibliothèque 'insane'."
msgid "'Shift + left' click'' to generate a new destination file name." msgid "'Shift + left' click'' to generate a new destination file name."
msgstr "'Maj + clic gauche' pour générer un nouveau fichier de sortie." msgstr "'Maj + clic gauche' pour générer un nouveau fichier de sortie."
#: ../../XS7.cpp:69 ../../XS7.cpp:104 #: ../../XS7.cpp:69 ../../XS7.cpp:104 ../../XS7.cpp:105 ../../XS7.cpp:107
msgid "Could not launch default file manager" msgid "Could not launch default file manager"
msgstr "Échec de lancement du gestionnaire de fichier par défaut." msgstr "Échec de lancement du gestionnaire de fichier par défaut."
#: ../../XS7.cpp:93 ../../XS7.cpp:128 #: ../../XS7.cpp:93 ../../XS7.cpp:128 ../../XS7.cpp:129 ../../XS7.cpp:131
msgid "Invalid folder name." msgid "Invalid folder name."
msgstr "Nom de dossier invalide." msgstr "Nom de dossier invalide."
#: ../../XS7.cpp:101 ../../XS7.cpp:136 #: ../../XS7.cpp:101 ../../XS7.cpp:136 ../../XS7.cpp:137 ../../XS7.cpp:139
msgid "Invalid file basename." msgid "Invalid file basename."
msgstr "Nom de base de fichier invalide." msgstr "Nom de base de fichier invalide."
#: ../../XS7.cpp:123 ../../XS7.cpp:158 #: ../../XS7.cpp:123 ../../XS7.cpp:158 ../../XS7.cpp:159 ../../XS7.cpp:161
msgid "Copyright: Saleem Edah-Tally [Surgeon] [Hobbyist developer]\n" msgid "Copyright: Saleem Edah-Tally [Surgeon] [Hobbyist developer]\n"
msgstr "Copyright: Saleem Edah-Tally [Chirurgien] [Développeur par hobby]\n" msgstr "Copyright: Saleem Edah-Tally [Chirurgien] [Développeur par hobby]\n"
#: ../../XS7.cpp:124 ../../XS7.cpp:159 #: ../../XS7.cpp:124 ../../XS7.cpp:159 ../../XS7.cpp:160 ../../XS7.cpp:162
msgid "License: CeCILL/CeCILL-C per file header." msgid "License: CeCILL/CeCILL-C per file header."
msgstr "Licence : CeCILL/CeCILL-C selon les entêtes de fichier." msgstr "Licence : CeCILL/CeCILL-C selon les entêtes de fichier."
@@ -224,6 +276,9 @@ msgid "S7"
msgstr "S7" msgstr "S7"
#: ../../Resources/InsaneWidget/UI/InsaneWidget.h:40 #: ../../Resources/InsaneWidget/UI/InsaneWidget.h:40
#: ../../Resources/InsaneWidget/UI/InsaneWidget.h:45
#: ../../Resources/InsaneWidget/UI/InsaneWidget.h:49
#: ../../Resources/InsaneWidget/UI/InsaneWidget.h:41
msgid "InsaneWidget" msgid "InsaneWidget"
msgstr "InsaneWidget" msgstr "InsaneWidget"
@@ -257,23 +312,32 @@ msgstr "Format de page :"
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:332 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:332
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:316 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:316
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:355
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:354
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:350
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:351
msgid "Double sided:" msgid "Double sided:"
msgstr "Recto-verso :" msgstr "Recto-verso :"
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:334 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:334
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:318 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:318
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:357
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:356
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:352
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:353
msgid "Total:" msgid "Total:"
msgstr "Total :" msgstr "Total :"
#: ../../XS7.cpp:46 ../../XS7.cpp:81 #: ../../XS7.cpp:46 ../../XS7.cpp:81 ../../XS7.cpp:82 ../../XS7.cpp:84
msgid "'Shift + left' click to generate a new destination file name." msgid "'Shift + left' click to generate a new destination file name."
msgstr "'Maj + clic gauche' pour générer un nouveau fichier de sortie." msgstr "'Maj + clic gauche' pour générer un nouveau fichier de sortie."
#: ../../XS7.cpp:121 ../../XS7.cpp:45 ../../XS7.cpp:156 #: ../../XS7.cpp:121 ../../XS7.cpp:45 ../../XS7.cpp:156 ../../XS7.cpp:157
#: ../../XS7.cpp:47 ../../XS7.cpp:159
msgid " - version " msgid " - version "
msgstr " - version " msgstr " - version "
#: ../../XS7.cpp:122 ../../XS7.cpp:157 #: ../../XS7.cpp:122 ../../XS7.cpp:157 ../../XS7.cpp:158 ../../XS7.cpp:160
msgid "" msgid ""
", using InsaneWidget.\n" ", using InsaneWidget.\n"
"\n" "\n"
@@ -282,21 +346,222 @@ msgstr ""
"\n" "\n"
#: ../../Resources/InsaneWidget/XScannerWidget.cpp:73 #: ../../Resources/InsaneWidget/XScannerWidget.cpp:73
#: ../../Resources/InsaneWidget/XScannerWidget.cpp:85
#: ../../Resources/InsaneWidget/XScannerWidget.cpp:87
msgid " device(s) found." msgid " device(s) found."
msgstr " périphériques trouvé(s)." msgstr " périphériques trouvé(s)."
#: ../../XS7.cpp:29 #: ../../XS7.cpp:29 ../../XS7.cpp:31
msgid "Config file tag." msgid "Config file tag."
msgstr "Suffixe du fichier de configuration." msgstr "Suffixe du fichier de configuration."
#: ../../XS7.cpp:30 #: ../../XS7.cpp:30 ../../XS7.cpp:32
msgid "Show version and quit." msgid "Show version and quit."
msgstr "Afficher la version et quitter." msgstr "Afficher la version et quitter."
#: ../../XS7.cpp:31 #: ../../XS7.cpp:31 ../../XS7.cpp:33
msgid "Show help and quit." msgid "Show help and quit."
msgstr "Afficher l'aide et quitter." msgstr "Afficher l'aide et quitter."
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:352 #: ../../Resources/InsaneWidget/XInsaneWidget.cpp:352
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:391
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:390
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:408
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:409
msgid "Destination file missing." msgid "Destination file missing."
msgstr "Fichier de destination manquant." msgstr "Fichier de destination manquant."
#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:130
#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:131
#: ../../Resources/StampWidget/UI/StampWidget.cpp:140
msgid ""
"Create a stamp with this text, which can be multiline.\n"
"\n"
"CTRL + S: save the current text.\n"
"CTRL + R: restore the saved text."
msgstr ""
"Créer un tampon utilisant ce texte qui peut être multi-ligne.\n"
"\n"
"CTRL + S : sauvegarder le texte actuel.\n"
"CTRL + R : restaurer le texte sauvegardé."
#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:138
#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:139
#: ../../Resources/StampWidget/UI/StampWidget.cpp:188
msgid "Select the font of the stamp text."
msgstr "Sélectionnez la police de caractère du texte."
#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:143
#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:144
msgid "Select the colour of the stamp text."
msgstr "Sélectionnez la couleur du texte."
#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:148
#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:155
#: ../../Resources/StampWidget/UI/StampWidget.cpp:199
msgid "Select the rotation angle of the stamp text."
msgstr "Sélectionnez l'angle de rotation du texte."
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:144
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:147
msgid "Stamp"
msgstr "Tampon"
#: ../../Resources/InsaneWidget/PixelToImageWriter.cpp:34
#: ../../Resources/InsaneWidget/PixelToImageWriter.cpp:35
msgid "Failed to read raw file."
msgstr "Échec de lecture du fichier brut."
#: ../../Resources/InsaneWidget/PixelToImageWriter.cpp:60
#: ../../Resources/InsaneWidget/PixelToImageWriter.cpp:68
#: ../../Resources/InsaneWidget/PixelToImageWriter.cpp:69
msgid "Unhandled output image format."
msgstr "Format de fichier d'image en sortie non pris en charge."
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:187
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:188
msgid "Failed to create output image."
msgstr "Échec de création de l'image de sortie."
#: ../../Resources/InsaneWidget/UI/StampWidget.h:46
#: ../../Resources/InsaneWidget/UI/StampWidget.h:47
#: ../../Resources/StampWidget/UI/StampWidget.h:57
msgid "StampWidget"
msgstr ""
#: ../../Resources/InsaneWidget/UI/StampWidget.cpp:150
#: ../../Resources/StampWidget/UI/StampWidget.cpp:171
msgid "Location of the stamp on the output."
msgstr "Place du tampon dans la sortie."
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:156
#: ../../Resources/InsaneWidget/UI/StampWidgets.cpp:127
#: ../../Resources/StampWidget/UI/StampWidgets.cpp:127
msgid "+"
msgstr ""
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:158
#: ../../Resources/InsaneWidget/UI/StampWidgets.cpp:129
#: ../../Resources/StampWidget/UI/StampWidgets.cpp:129
msgid "Add a stamp widget."
msgstr "Ajouter un widget 'Tampon'."
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:161
#: ../../Resources/InsaneWidget/UI/StampWidgets.cpp:132
#: ../../Resources/StampWidget/UI/StampWidgets.cpp:132
msgid "-"
msgstr ""
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:163
#: ../../Resources/InsaneWidget/UI/StampWidgets.cpp:134
#: ../../Resources/StampWidget/UI/StampWidgets.cpp:134
msgid "Remove the selected stamp widget."
msgstr "Supprimer le widget 'Tampon' sélectionné."
#: ../../Resources/InsaneWidget/XStampWidget.cpp:56
#: ../../Resources/InsaneWidget/XStampWidget.cpp:57
#: ../../Resources/StampWidget/XStampWidget.cpp:76
msgid "Centre"
msgstr "Centre"
#: ../../Resources/InsaneWidget/XStampWidget.cpp:57
#: ../../Resources/InsaneWidget/XStampWidget.cpp:58
#: ../../Resources/StampWidget/XStampWidget.cpp:77
msgid "North"
msgstr "Nord"
#: ../../Resources/InsaneWidget/XStampWidget.cpp:57
#: ../../Resources/InsaneWidget/XStampWidget.cpp:58
#: ../../Resources/StampWidget/XStampWidget.cpp:77
msgid "South"
msgstr "Sud"
#: ../../Resources/InsaneWidget/XStampWidget.cpp:57
#: ../../Resources/InsaneWidget/XStampWidget.cpp:58
#: ../../Resources/StampWidget/XStampWidget.cpp:77
msgid "West"
msgstr "Ouest"
#: ../../Resources/InsaneWidget/XStampWidget.cpp:58
#: ../../Resources/InsaneWidget/XStampWidget.cpp:59
#: ../../Resources/StampWidget/XStampWidget.cpp:78
msgid "North-east"
msgstr "Nord-est"
#: ../../Resources/InsaneWidget/XStampWidget.cpp:58
#: ../../Resources/InsaneWidget/XStampWidget.cpp:59
#: ../../Resources/StampWidget/XStampWidget.cpp:78
msgid "North-west"
msgstr "Nord-ouest"
#: ../../Resources/InsaneWidget/XStampWidget.cpp:59
#: ../../Resources/InsaneWidget/XStampWidget.cpp:60
#: ../../Resources/StampWidget/XStampWidget.cpp:79
msgid "South-east"
msgstr "Sud-est"
#: ../../Resources/InsaneWidget/XStampWidget.cpp:59
#: ../../Resources/InsaneWidget/XStampWidget.cpp:60
#: ../../Resources/StampWidget/XStampWidget.cpp:79
msgid "South-west"
msgstr "Sud-ouest"
#: ../../Resources/InsaneWidget/XStampWidget.cpp:57
#: ../../Resources/InsaneWidget/XStampWidget.cpp:58
#: ../../Resources/StampWidget/XStampWidget.cpp:77
msgid "East"
msgstr "Est"
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:137
#: ../../Resources/InsaneWidget/UI/InsaneWidget.cpp:141
msgid ""
"'Left click' to start the scan project.\n"
"'Right click' to show the scanner widget.\n"
"'Ctrl + Right click' to show the Stamp dialog."
msgstr ""
"'Clic gauche' pour démarrer la numérisation.\n"
"'Clic droit' pour afficher les options du numériseur.\n"
"'Ctrl + Clic droit' pour définir un tampon."
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:376
#: ../../Resources/InsaneWidget/XInsaneWidget.cpp:377
msgid "Stamps"
msgstr "Tampons"
#: ../../Resources/InsaneWidget/UI/StampWidgets.h:44
#: ../../Resources/StampWidget/UI/StampWidgets.h:44
msgid "StampWidgets"
msgstr ""
#: ../../Resources/StampWidget/UI/StampWidget.cpp:149
msgid "Foreground:"
msgstr "Avant-plan :"
#: ../../Resources/StampWidget/UI/StampWidget.cpp:154
msgid "Foreground colour of the text."
msgstr "Couleur du texte."
#: ../../Resources/StampWidget/UI/StampWidget.cpp:157
msgid "Background:"
msgstr "Arrière-plan :"
#: ../../Resources/StampWidget/UI/StampWidget.cpp:162
msgid "Background colour of the text."
msgstr "Couleur de l'arrière-plan."
#: ../../Resources/StampWidget/UI/StampWidget.cpp:165
msgid "Location:"
msgstr "Position :"
#: ../../Resources/StampWidget/UI/StampWidget.cpp:177
msgid "Click to update the preview."
msgstr "Cliquez pour actualiser l'aperçu."
#: ../../Resources/StampWidget/UI/StampWidget.cpp:191
msgid "Transparent"
msgstr "Transparent"
#: ../../Resources/StampWidget/UI/StampWidget.cpp:194
msgid ""
"Check for a completely transparent background and for a transparent text."
msgstr "Activez la transparence du texte et de l'arrière-plan. "

View File

@@ -1,7 +1,7 @@
#Simple PKGBUILD that suits my need. #Simple PKGBUILD that suits my need.
pkgname=(s7) pkgname=(s7)
pkgver=1 pkgver=2
pkgrel=0 pkgrel=0
arch=('x86_64' 'i686' 'armv7h' 'aarch64') arch=('x86_64' 'i686' 'armv7h' 'aarch64')
url='http://github.com/nmset/s7/' url='http://github.com/nmset/s7/'

View File

@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.5)
project(StampWidget)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
find_package(wxWidgets COMPONENTS base core CONFIG REQUIRED)
include_directories(${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/UI)
add_library(stampwidget STATIC
DefsStampWidget.h
UI/StampWidget.cpp
UI/StampWidgets.cpp
XStampWidget.cpp
XStampWidgets.cpp
StampWorker.cpp)
target_link_libraries(stampwidget
${wxWidgets_LIBRARIES})

View File

@@ -0,0 +1,30 @@
// /*
// * File: Common.h
// * Author: Saleem Edah-Tally - nmset@yandex.com
// * License : CeCILL-C
// * Copyright Saleem Edah-Tally - © 2025
// *
// * Created on 27 06 2025, 20:34
// */
#ifndef COMMON_H
#define COMMON_H
#include <wx/wx.h>
// ----------------------------------------------------------------------------
enum {CENTRE = 0, NORTH, SOUTH, EAST, WEST,
NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST};
struct StampDescriptor
{
wxImage image;
wxString text;
wxFont font = wxNullFont;
wxColour foregroundColour = wxNullColour;
wxColour backgroundColour = wxNullColour;
int rotationAngle = 45;
int location = 0;
bool transparent = true;
};
#endif // COMMON_H

View File

@@ -0,0 +1,121 @@
// /*
// * File: StampWorker.cpp
// * Author: Saleem Edah-Tally - nmset@yandex.com
// * License : CeCILL-C
// * Copyright Saleem Edah-Tally - © 2025
// *
// * Created on 02 07 2025, 21:23
// */
#include "StampWorker.h"
#include <math.h> // M_PI
wxImage StampWorker::CreateStamp(StampDescriptor * descriptor, int scanResolution)
{
/*
* Rescale the font point size of the stamp according to the scan resolution.
* Exclude rescaling on v4l that does not have a resolution parameter.
*/
wxFont dcFont(descriptor->font);
double scale = (scanResolution > 0)
? (double) scanResolution / 72.0
:1.0;
wxCoord extentWidth, extentHeight, textLineHeight;
{
wxMemoryDC dc;
if (scale != 1.0)
dcFont.SetFractionalPointSize(descriptor->font.GetFractionalPointSize() * scale);
dc.SetFont(dcFont); // Mandatory despite set below.
dc.GetMultiLineTextExtent(descriptor->text, &extentWidth, &extentHeight, &textLineHeight, &dcFont);
}
wxBitmap bmp;
// bmp.UseAlpha(true); // Not mandatory.
bmp.Create(extentWidth, extentHeight); // Memo: See CreateWithDIPSize
wxImage img0, img;
wxMemoryDC dc(bmp);
dc.SetFont(dcFont);
if (descriptor->transparent)
{
// +++, Use the red channel only: used by ConvertColourToAlpha().
// The chosen value is inversely proportional to the transparency of the text itself.
dc.SetTextForeground(wxColour(128, 0, 0)); // A good mean is reasonable.
dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
}
else
{
dc.SetTextForeground(descriptor->foregroundColour);
dc.SetTextBackground(descriptor->backgroundColour);
dc.SetBackgroundMode(wxBRUSHSTYLE_SOLID);
}
dc.DrawText(descriptor->text, wxPoint(0, 0));
img0 = bmp.ConvertToImage();
if (!descriptor->transparent)
img0.InitAlpha(); // So that there is no dark surrounding if it is rotated.
img = img0.Rotate((M_PI / 180.0) * descriptor->rotationAngle, wxPoint(0, 0));
if (descriptor->transparent)
{
img.ConvertColourToAlpha(descriptor->foregroundColour.GetRed(),
descriptor->foregroundColour.GetGreen(),
descriptor->foregroundColour.GetBlue()); // The target colour is passed in.
img.ChangeSaturation(-0.7); // Slightly better looking.
}
return img;
}
void StampWorker::StampBackground(wxImage& background,
const wxImage& stamp, int location)
{
// Simply pasting the stamp on the background, i.e., the scanned page.
wxPoint bgCentre(background.GetWidth() / 2, background.GetHeight() / 2);
wxPoint stampCentre(stamp.GetWidth() / 2, stamp.GetHeight() / 2);
int x = 0, y = 0;
switch (location)
{
case NORTH:
x = bgCentre.x - stampCentre.x;
y = 0;
break;
case SOUTH:
x = bgCentre.x - stampCentre.x;
y = background.GetHeight() - stamp.GetHeight();
break;
case EAST:
x = background.GetWidth() - stamp.GetWidth();
y = bgCentre.y - stampCentre.y;
break;
case WEST:
x = 0;
y = bgCentre.y - stampCentre.y;
break;
case NORTH_EAST:
x = background.GetWidth() - stamp.GetWidth();
y = 0;
break;
case NORTH_WEST:
x = 0;
y = 0;
break;
case SOUTH_EAST:
x = background.GetWidth() - stamp.GetWidth();
y = background.GetHeight() - stamp.GetHeight();
break;
case SOUTH_WEST:
x = 0;
y = background.GetHeight() - stamp.GetHeight();
break;
default: // CENTRE
x = bgCentre.x - stampCentre.x;
y = bgCentre.y - stampCentre.y;
break;
}
wxPoint stampLocation(x, y);
background.Paste(stamp, stampLocation.x, stampLocation.y, wxIMAGE_ALPHA_BLEND_COMPOSE);
}

View File

@@ -0,0 +1,38 @@
// /*
// * File: StampWorker.h
// * Author: Saleem Edah-Tally - nmset@yandex.com
// * License : CeCILL-C
// * Copyright Saleem Edah-Tally - © 2025
// *
// * Created on 02 07 2025, 21:23
// */
#ifndef STAMPWORKER_H
#define STAMPWORKER_H
#include <wx/wx.h>
#include "DefsStampWidget.h"
struct StampDescriptor;
/**
* A stamp is understood here as\n
* - a transparent text in a transparent frame with no borders\n
* - an opaque text on an opaque background with no borders.\n
*
* The text may be rotated. Actually, an initial image with the text is rotated
* and its new orthogonal bounds accepted (fortunately, we don't have to compute
* that).\n
* The font point size is rescaled to match the scan resolution. If the
* scanResolution parameter is invalid (<=0), the font is not rescaled.
*/
class StampWorker
{
DECLARE_DYNAMIC_CLASS( StampWorker )
public:
static wxImage CreateStamp(StampDescriptor * descriptor, int scanResolution = -1);
static void StampBackground(wxImage& background, const wxImage& stamp, int location = CENTRE);
};
#endif // STAMPWORKER_H

View File

@@ -0,0 +1,239 @@
/////////////////////////////////////////////////////////////////////////////
// Name: StampWidget.cpp
// Purpose:
// Author: Saleem EDAH-TALLY
// Modified by:
// Created: mar. 01 juil. 2025 19:14:05
// RCS-ID:
// Copyright: Copyright Saleem EDAH-TALLY. All rights reserved.
// Licence:
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
////@begin includes
////@end includes
#include "StampWidget.h"
////@begin XPM images
////@end XPM images
/*
* StampWidget type definition
*/
IMPLEMENT_DYNAMIC_CLASS( StampWidget, wxPanel )
/*
* StampWidget event table definition
*/
BEGIN_EVENT_TABLE( StampWidget, wxPanel )
////@begin StampWidget event table entries
////@end StampWidget event table entries
END_EVENT_TABLE()
/*
* StampWidget constructors
*/
StampWidget::StampWidget()
{
Init();
}
StampWidget::StampWidget( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style )
{
Init();
Create(parent, id, pos, size, style);
}
/*
* StampWidget creator
*/
bool StampWidget::Create( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style )
{
////@begin StampWidget creation
SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
wxPanel::Create( parent, id, pos, size, style );
CreateControls();
if (GetSizer())
{
GetSizer()->SetSizeHints(this);
}
Centre();
////@end StampWidget creation
return true;
}
/*
* StampWidget destructor
*/
StampWidget::~StampWidget()
{
////@begin StampWidget destruction
////@end StampWidget destruction
}
/*
* Member initialisation
*/
void StampWidget::Init()
{
////@begin StampWidget member initialisation
szStampWidgetMain = NULL;
txtStamp = NULL;
szStampWidgetH0 = NULL;
szStampWidgetFlexGrid = NULL;
lblForegroundColour = NULL;
cpkForegroundStamp = NULL;
lblBackgroundColour = NULL;
cpkBackgroundStamp = NULL;
lblLocation = NULL;
cmbStampLocation = NULL;
panBitmapPreview = NULL;
szBitmapPreviewInPanel = NULL;
szStampWidgetH1 = NULL;
fpkStamp = NULL;
tglTransparent = NULL;
sldTextRotationAngle = NULL;
////@end StampWidget member initialisation
}
/*
* Control creation for StampWidget
*/
void StampWidget::CreateControls()
{
////@begin StampWidget content construction
StampWidget* itemPanel1 = this;
szStampWidgetMain = new wxBoxSizer(wxVERTICAL);
itemPanel1->SetSizer(szStampWidgetMain);
txtStamp = new wxTextCtrl( itemPanel1, ID_TEXTCTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE );
if (StampWidget::ShowToolTips())
txtStamp->SetToolTip(_("Create a stamp with this text, which can be multiline.\n\nCTRL + S: save the current text.\nCTRL + R: restore the saved text."));
szStampWidgetMain->Add(txtStamp, 0, wxGROW|wxALL, 5);
szStampWidgetH0 = new wxBoxSizer(wxHORIZONTAL);
szStampWidgetMain->Add(szStampWidgetH0, 0, wxALIGN_LEFT|wxALL, 5);
szStampWidgetFlexGrid = new wxFlexGridSizer(0, 2, 0, 0);
szStampWidgetH0->Add(szStampWidgetFlexGrid, 1, wxGROW|wxALL, 5);
lblForegroundColour = new wxStaticText( itemPanel1, ID_STATIC_FOREGROUND_COLOUR, _("Foreground:"), wxDefaultPosition, wxDefaultSize, 0 );
szStampWidgetFlexGrid->Add(lblForegroundColour, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
cpkForegroundStamp = new wxColourPickerCtrl( itemPanel1, ID_COLOURCTRL_FOREGROUND, wxColour(), wxDefaultPosition, wxDefaultSize, wxCLRP_DEFAULT_STYLE );
if (StampWidget::ShowToolTips())
cpkForegroundStamp->SetToolTip(_("Foreground colour of the text."));
szStampWidgetFlexGrid->Add(cpkForegroundStamp, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
lblBackgroundColour = new wxStaticText( itemPanel1, ID_STATIC_BACKGROUND_COLOUR, _("Background:"), wxDefaultPosition, wxDefaultSize, 0 );
szStampWidgetFlexGrid->Add(lblBackgroundColour, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
cpkBackgroundStamp = new wxColourPickerCtrl( itemPanel1, ID_COLOURCTRL_BACKGROUND, wxColour(255, 255, 255), wxDefaultPosition, wxDefaultSize, wxCLRP_DEFAULT_STYLE );
if (StampWidget::ShowToolTips())
cpkBackgroundStamp->SetToolTip(_("Background colour of the text."));
szStampWidgetFlexGrid->Add(cpkBackgroundStamp, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
lblLocation = new wxStaticText( itemPanel1, ID_STATIC_LOCATION, _("Location:"), wxDefaultPosition, wxDefaultSize, 0 );
szStampWidgetFlexGrid->Add(lblLocation, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
wxArrayString cmbStampLocationStrings;
cmbStampLocation = new wxComboBox( itemPanel1, ID_COMBOBOX, wxEmptyString, wxDefaultPosition, wxDefaultSize, cmbStampLocationStrings, wxCB_READONLY );
if (StampWidget::ShowToolTips())
cmbStampLocation->SetToolTip(_("Location of the stamp on the output."));
szStampWidgetFlexGrid->Add(cmbStampLocation, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL|wxALL, 5);
panBitmapPreview = new wxPanel( itemPanel1, ID_PANEL_BITMAP_PREVIEW, wxDefaultPosition, wxSize(200, 200), wxSUNKEN_BORDER|wxTAB_TRAVERSAL );
panBitmapPreview->SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
if (StampWidget::ShowToolTips())
panBitmapPreview->SetToolTip(_("Click to update the preview."));
szStampWidgetH0->Add(panBitmapPreview, 0, wxALIGN_TOP|wxALL, 5);
szBitmapPreviewInPanel = new wxBoxSizer(wxVERTICAL);
panBitmapPreview->SetSizer(szBitmapPreviewInPanel);
szStampWidgetH1 = new wxBoxSizer(wxHORIZONTAL);
szStampWidgetMain->Add(szStampWidgetH1, 0, wxGROW|wxALL, 5);
fpkStamp = new wxFontPickerCtrl( itemPanel1, ID_FONTCTRL, wxFont(), wxDefaultPosition, wxDefaultSize, wxFNTP_FONTDESC_AS_LABEL );
if (StampWidget::ShowToolTips())
fpkStamp->SetToolTip(_("Select the font of the stamp text."));
szStampWidgetH1->Add(fpkStamp, 1, wxGROW|wxALL, 5);
tglTransparent = new wxToggleButton( itemPanel1, ID_TOGGLE_TRANSPARENT, _("Transparent"), wxDefaultPosition, wxDefaultSize, 0 );
tglTransparent->SetValue(true);
if (StampWidget::ShowToolTips())
tglTransparent->SetToolTip(_("Check for a completely transparent background and for a transparent text."));
szStampWidgetH1->Add(tglTransparent, 0, wxGROW|wxALL, 5);
sldTextRotationAngle = new wxSlider( itemPanel1, ID_SLIDER, 45, -180, 180, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL|wxSL_AUTOTICKS|wxSL_LABELS );
if (StampWidget::ShowToolTips())
sldTextRotationAngle->SetToolTip(_("Select the rotation angle of the stamp text."));
szStampWidgetMain->Add(sldTextRotationAngle, 0, wxGROW|wxALL, 5);
////@end StampWidget content construction
}
/*
* Should we show tooltips?
*/
bool StampWidget::ShowToolTips()
{
return true;
}
/*
* Get bitmap resources
*/
wxBitmap StampWidget::GetBitmapResource( const wxString& name )
{
// Bitmap retrieval
////@begin StampWidget bitmap retrieval
wxUnusedVar(name);
return wxNullBitmap;
////@end StampWidget bitmap retrieval
}
/*
* Get icon resources
*/
wxIcon StampWidget::GetIconResource( const wxString& name )
{
// Icon retrieval
////@begin StampWidget icon retrieval
wxUnusedVar(name);
return wxNullIcon;
////@end StampWidget icon retrieval
}

View File

@@ -0,0 +1,129 @@
/////////////////////////////////////////////////////////////////////////////
// Name: StampWidget.h
// Purpose:
// Author: Saleem EDAH-TALLY
// Modified by:
// Created: mar. 01 juil. 2025 19:14:05
// RCS-ID:
// Copyright: Copyright Saleem EDAH-TALLY. All rights reserved.
// Licence:
/////////////////////////////////////////////////////////////////////////////
#ifndef _STAMPWIDGET_H_
#define _STAMPWIDGET_H_
/*!
* Includes
*/
////@begin includes
#include "wx/clrpicker.h"
#include "wx/fontpicker.h"
#include "wx/tglbtn.h"
////@end includes
#include "wx/combobox.h"
#include "wx/stattext.h"
/*!
* Forward declarations
*/
////@begin forward declarations
class wxBoxSizer;
class wxFlexGridSizer;
class wxColourPickerCtrl;
class wxFontPickerCtrl;
class wxToggleButton;
////@end forward declarations
/*!
* Control identifiers
*/
////@begin control identifiers
#define ID_STAMPWIDGET 10000
#define ID_TEXTCTRL 10001
#define ID_STATIC_FOREGROUND_COLOUR 10008
#define ID_COLOURCTRL_FOREGROUND 10003
#define ID_STATIC_BACKGROUND_COLOUR 10009
#define ID_COLOURCTRL_BACKGROUND 10006
#define ID_STATIC_LOCATION 10010
#define ID_COMBOBOX 10005
#define ID_PANEL_BITMAP_PREVIEW 10011
#define ID_FONTCTRL 10002
#define ID_TOGGLE_TRANSPARENT 10007
#define ID_SLIDER 10004
#define SYMBOL_STAMPWIDGET_STYLE wxTAB_TRAVERSAL
#define SYMBOL_STAMPWIDGET_TITLE _("StampWidget")
#define SYMBOL_STAMPWIDGET_IDNAME ID_STAMPWIDGET
#define SYMBOL_STAMPWIDGET_SIZE wxSize(400, 300)
#define SYMBOL_STAMPWIDGET_POSITION wxDefaultPosition
////@end control identifiers
#include <wx/panel.h>
#include <wx/slider.h>
/*!
* StampWidget class declaration
*/
class StampWidget: public wxPanel
{
DECLARE_DYNAMIC_CLASS( StampWidget )
DECLARE_EVENT_TABLE()
public:
/// Constructors
StampWidget();
StampWidget( wxWindow* parent, wxWindowID id = SYMBOL_STAMPWIDGET_IDNAME, const wxPoint& pos = SYMBOL_STAMPWIDGET_POSITION, const wxSize& size = SYMBOL_STAMPWIDGET_SIZE, long style = SYMBOL_STAMPWIDGET_STYLE );
/// Creation
bool Create( wxWindow* parent, wxWindowID id = SYMBOL_STAMPWIDGET_IDNAME, const wxPoint& pos = SYMBOL_STAMPWIDGET_POSITION, const wxSize& size = SYMBOL_STAMPWIDGET_SIZE, long style = SYMBOL_STAMPWIDGET_STYLE );
/// Destructor
~StampWidget();
/// Initialises member variables
void Init();
/// Creates the controls and sizers
void CreateControls();
////@begin StampWidget event handler declarations
////@end StampWidget event handler declarations
////@begin StampWidget member function declarations
/// Retrieves bitmap resources
wxBitmap GetBitmapResource( const wxString& name );
/// Retrieves icon resources
wxIcon GetIconResource( const wxString& name );
////@end StampWidget member function declarations
/// Should we show tooltips?
static bool ShowToolTips();
////@begin StampWidget member variables
wxBoxSizer* szStampWidgetMain;
wxTextCtrl* txtStamp;
wxBoxSizer* szStampWidgetH0;
wxFlexGridSizer* szStampWidgetFlexGrid;
wxStaticText* lblForegroundColour;
wxColourPickerCtrl* cpkForegroundStamp;
wxStaticText* lblBackgroundColour;
wxColourPickerCtrl* cpkBackgroundStamp;
wxStaticText* lblLocation;
wxComboBox* cmbStampLocation;
wxPanel* panBitmapPreview;
wxBoxSizer* szBitmapPreviewInPanel;
wxBoxSizer* szStampWidgetH1;
wxFontPickerCtrl* fpkStamp;
wxToggleButton* tglTransparent;
wxSlider* sldTextRotationAngle;
////@end StampWidget member variables
};
#endif
// _STAMPWIDGET_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
#include "wx/msw/wx.rc"

View File

@@ -0,0 +1,178 @@
/////////////////////////////////////////////////////////////////////////////
// Name: StampWidgets.cpp
// Purpose:
// Author: Saleem EDAH-TALLY
// Modified by:
// Created: dim. 06 juil. 2025 22:33:34
// RCS-ID:
// Copyright: Copyright Saleem EDAH-TALLY. All rights reserved.
// Licence:
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
////@begin includes
#include "wx/imaglist.h"
////@end includes
#include "StampWidgets.h"
////@begin XPM images
////@end XPM images
/*
* StampWidgets type definition
*/
IMPLEMENT_DYNAMIC_CLASS( StampWidgets, wxPanel )
/*
* StampWidgets event table definition
*/
BEGIN_EVENT_TABLE( StampWidgets, wxPanel )
////@begin StampWidgets event table entries
////@end StampWidgets event table entries
END_EVENT_TABLE()
/*
* StampWidgets constructors
*/
StampWidgets::StampWidgets()
{
Init();
}
StampWidgets::StampWidgets( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style )
{
Init();
Create(parent, id, pos, size, style);
}
/*
* StampWidgets creator
*/
bool StampWidgets::Create( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style )
{
////@begin StampWidgets creation
SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
wxPanel::Create( parent, id, pos, size, style );
CreateControls();
Centre();
////@end StampWidgets creation
return true;
}
/*
* StampWidgets destructor
*/
StampWidgets::~StampWidgets()
{
////@begin StampWidgets destruction
////@end StampWidgets destruction
}
/*
* Member initialisation
*/
void StampWidgets::Init()
{
////@begin StampWidgets member initialisation
szNoteBookMain = NULL;
szNoteBookButtons = NULL;
btnAddStampWidget = NULL;
btnDeleteStampWidget = NULL;
nbStampWidgets = NULL;
////@end StampWidgets member initialisation
}
/*
* Control creation for StampWidgets
*/
void StampWidgets::CreateControls()
{
////@begin StampWidgets content construction
StampWidgets* itemPanel1 = this;
szNoteBookMain = new wxBoxSizer(wxHORIZONTAL);
itemPanel1->SetSizer(szNoteBookMain);
szNoteBookButtons = new wxBoxSizer(wxHORIZONTAL);
szNoteBookMain->Add(szNoteBookButtons, 0, wxALIGN_TOP, 5);
btnAddStampWidget = new wxButton( itemPanel1, ID_BUTTON_NB, _("+"), wxDefaultPosition, wxSize(30, -1), 0 );
if (StampWidgets::ShowToolTips())
btnAddStampWidget->SetToolTip(_("Add a stamp widget."));
szNoteBookButtons->Add(btnAddStampWidget, 0, wxALIGN_CENTER_VERTICAL|wxFIXED_MINSIZE, 1);
btnDeleteStampWidget = new wxButton( itemPanel1, ID_BUTTON_NB1, _("-"), wxDefaultPosition, wxSize(30, -1), 0 );
if (StampWidgets::ShowToolTips())
btnDeleteStampWidget->SetToolTip(_("Remove the selected stamp widget."));
szNoteBookButtons->Add(btnDeleteStampWidget, 0, wxALIGN_CENTER_VERTICAL, 1);
nbStampWidgets = new wxNotebook( itemPanel1, ID_NOTEBOOK_STAMPWIDGETS_, wxDefaultPosition, wxDefaultSize, wxBK_DEFAULT );
szNoteBookMain->Add(nbStampWidgets, 1, wxGROW|wxALL, 5);
////@end StampWidgets content construction
}
/*
* Should we show tooltips?
*/
bool StampWidgets::ShowToolTips()
{
return true;
}
/*
* Get bitmap resources
*/
wxBitmap StampWidgets::GetBitmapResource( const wxString& name )
{
// Bitmap retrieval
////@begin StampWidgets bitmap retrieval
wxUnusedVar(name);
return wxNullBitmap;
////@end StampWidgets bitmap retrieval
}
/*
* Get icon resources
*/
wxIcon StampWidgets::GetIconResource( const wxString& name )
{
// Icon retrieval
////@begin StampWidgets icon retrieval
wxUnusedVar(name);
return wxNullIcon;
////@end StampWidgets icon retrieval
}

View File

@@ -0,0 +1,103 @@
/////////////////////////////////////////////////////////////////////////////
// Name: StampWidgets.h
// Purpose:
// Author: Saleem EDAH-TALLY
// Modified by:
// Created: dim. 06 juil. 2025 22:33:34
// RCS-ID:
// Copyright: Copyright Saleem EDAH-TALLY. All rights reserved.
// Licence:
/////////////////////////////////////////////////////////////////////////////
#ifndef _STAMPWIDGETS_H_
#define _STAMPWIDGETS_H_
/*!
* Includes
*/
////@begin includes
#include "wx/notebook.h"
////@end includes
/*!
* Forward declarations
*/
////@begin forward declarations
class wxBoxSizer;
class wxNotebook;
////@end forward declarations
#include <wx/panel.h>
#include <wx/button.h>
/*!
* Control identifiers
*/
////@begin control identifiers
#define ID_STAMPWIDGETS 10000
#define ID_BUTTON_NB 10002
#define ID_BUTTON_NB1 10003
#define ID_NOTEBOOK_STAMPWIDGETS_ 10001
#define SYMBOL_STAMPWIDGETS_STYLE wxTAB_TRAVERSAL
#define SYMBOL_STAMPWIDGETS_TITLE _("StampWidgets")
#define SYMBOL_STAMPWIDGETS_IDNAME ID_STAMPWIDGETS
#define SYMBOL_STAMPWIDGETS_SIZE wxSize(400, 300)
#define SYMBOL_STAMPWIDGETS_POSITION wxDefaultPosition
////@end control identifiers
/*!
* StampWidgets class declaration
*/
class StampWidgets: public wxPanel
{
DECLARE_DYNAMIC_CLASS( StampWidgets )
DECLARE_EVENT_TABLE()
public:
/// Constructors
StampWidgets();
StampWidgets( wxWindow* parent, wxWindowID id = SYMBOL_STAMPWIDGETS_IDNAME, const wxPoint& pos = SYMBOL_STAMPWIDGETS_POSITION, const wxSize& size = SYMBOL_STAMPWIDGETS_SIZE, long style = SYMBOL_STAMPWIDGETS_STYLE );
/// Creation
bool Create( wxWindow* parent, wxWindowID id = SYMBOL_STAMPWIDGETS_IDNAME, const wxPoint& pos = SYMBOL_STAMPWIDGETS_POSITION, const wxSize& size = SYMBOL_STAMPWIDGETS_SIZE, long style = SYMBOL_STAMPWIDGETS_STYLE );
/// Destructor
~StampWidgets();
/// Initialises member variables
void Init();
/// Creates the controls and sizers
void CreateControls();
////@begin StampWidgets event handler declarations
////@end StampWidgets event handler declarations
////@begin StampWidgets member function declarations
/// Retrieves bitmap resources
wxBitmap GetBitmapResource( const wxString& name );
/// Retrieves icon resources
wxIcon GetIconResource( const wxString& name );
////@end StampWidgets member function declarations
/// Should we show tooltips?
static bool ShowToolTips();
////@begin StampWidgets member variables
wxBoxSizer* szNoteBookMain;
wxBoxSizer* szNoteBookButtons;
wxButton* btnAddStampWidget;
wxButton* btnDeleteStampWidget;
wxNotebook* nbStampWidgets;
////@end StampWidgets member variables
};
#endif
// _STAMPWIDGETS_H_

View File

@@ -0,0 +1,230 @@
// /*
// * File: XStampWidget.cpp
// * Author: Saleem Edah-Tally - nmset@yandex.com
// * License : CeCILL-C
// * Copyright Saleem Edah-Tally - © 2025
// *
// * Created on 01 07 2025, 20:35
// */
#include "XStampWidget.h"
#include "DefsStampWidget.h"
#include "StampWorker.h"
#include <wx/bmpbndl.h>
using namespace std;
IMPLEMENT_CLASS( XStampWidget, StampWidget )
XStampWidget::XStampWidget(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
: StampWidget(parent, id, pos, size, style)
{}
bool XStampWidget::Setup(wxConfig * config)
{
m_config = config;
if (!m_config)
return false;
const wxString fontDesc = m_config->Read("/Stamp/FontDesc", "");
if (!fontDesc.IsEmpty())
{
wxFont font = wxNullFont;
font.SetNativeFontInfo(fontDesc);
if (font.IsOk())
{
fpkStamp->SetSelectedFont(font);
fpkStamp->Update();
}
}
long rgb = m_config->Read("/Stamp/ForegroundRGB", wxNOT_FOUND);
if (rgb > wxNOT_FOUND)
{
wxColour foregroundColour = wxNullColour;
foregroundColour.SetRGB((wxUint32) rgb);
if (foregroundColour.IsOk())
{
cpkForegroundStamp->SetColour(foregroundColour);
}
}
rgb = m_config->Read("/Stamp/BackgroundRGB", wxNOT_FOUND);
if (rgb > wxNOT_FOUND)
{
wxColour backgroundColour = wxNullColour;
backgroundColour.SetRGB((wxUint32) rgb);
if (backgroundColour.IsOk())
{
cpkBackgroundStamp->SetColour(backgroundColour);
}
}
int textRotationAngle = m_config->Read("/Stamp/RotationAngle", 45);
sldTextRotationAngle->SetValue(textRotationAngle);
bool transparency = m_config->ReadBool("/Stamp/Transparency", true);
tglTransparent->SetValue(transparency);
wxCommandEvent evt;
evt.SetInt(transparency);
OnTransparencyToggled(evt);
fpkStamp->Bind(wxEVT_FONTPICKER_CHANGED, &XStampWidget::OnFontChanged, this);
cpkForegroundStamp->Bind(wxEVT_COLOURPICKER_CHANGED, &XStampWidget::OnForegroundColourChanged, this);
cpkBackgroundStamp->Bind(wxEVT_COLOURPICKER_CHANGED, &XStampWidget::OnBackgroundColourChanged, this);
// *End* of any mouse and any keyboard interaction.
sldTextRotationAngle->Bind(wxEVT_SCROLL_CHANGED, &XStampWidget::OnAngleSliderChanged, this);
txtStamp->Bind(wxEVT_KEY_UP, &XStampWidget::OnTxtKeyPressed, this);
tglTransparent->Bind(wxEVT_TOGGLEBUTTON, &XStampWidget::OnTransparencyToggled, this);
wxArrayString stampLocations ({_("Centre"),
_("North"), _("South"), _("East"), _("West"),
_("North-east"), _("North-west"),
_("South-east"), _("South-west")});
cmbStampLocation->Append(stampLocations);
int location = m_config->Read("/Stamp/Location", wxNOT_FOUND);
cmbStampLocation->Select((location >= cmbStampLocation->GetCount() || location < 0) ? 0 : location);
cmbStampLocation->Bind(wxEVT_COMMAND_COMBOBOX_SELECTED, &XStampWidget::OnLocationChanged, this);
// A panel container is used to have its borders.
m_sbmpPreview = new wxGenericStaticBitmap(panBitmapPreview, wxID_ANY, wxBitmapBundle(wxNullBitmap), wxDefaultPosition, panBitmapPreview->GetSize());
szBitmapPreviewInPanel->Add(m_sbmpPreview, 0, wxGROW | wxALL, 5);
m_sbmpPreview->SetScaleMode(wxStaticBitmapBase::Scale_AspectFit);
m_sbmpPreview->Bind(wxEVT_LEFT_UP, &XStampWidget::OnBitmapPreview, this);
return true;
}
void XStampWidget::OnFontChanged(wxFontPickerEvent& evt)
{
if (!m_config)
{
evt.Skip();
return;
}
const wxString desc = evt.GetFont().GetNativeFontInfoDesc();
m_config->Write("/Stamp/FontDesc", desc);
m_config->Flush();
evt.Skip();
}
void XStampWidget::OnForegroundColourChanged(wxColourPickerEvent& evt)
{
if (!m_config)
{
evt.Skip();
return;
}
const wxUint32 rgb = evt.GetColour().GetRGB();
m_config->Write("/Stamp/ForegroundRGB", (long) rgb);
m_config->Flush();
evt.Skip();
}
void XStampWidget::OnBackgroundColourChanged(wxColourPickerEvent& evt)
{
if (!m_config)
{
evt.Skip();
return;
}
const wxUint32 rgb = evt.GetColour().GetRGB();
m_config->Write("/Stamp/BackgroundRGB", (long) rgb);
m_config->Flush();
evt.Skip();
}
void XStampWidget::OnAngleSliderChanged(wxScrollEvent& evt)
{
if (!m_config)
{
evt.Skip();
return;
}
m_config->Write("/Stamp/RotationAngle", evt.GetInt());
m_config->Flush();
evt.Skip();
}
void XStampWidget::OnTxtKeyPressed(wxKeyEvent& evt)
{
if (!m_config || !evt.ControlDown())
{
evt.Skip();
return;
}
if (evt.GetKeyCode() == 'S')
{
if (txtStamp->IsEmpty())
{
evt.Skip();
return;
}
m_config->Write("/Stamp/Text", txtStamp->GetValue());
m_config->Flush();
}
else if (evt.GetKeyCode() == 'R')
{
wxString last;
if (m_config->Read("/Stamp/Text", &last))
{
txtStamp->SetValue(last);
txtStamp->SetSelection(last.Len(), last.Len());
}
}
evt.Skip();
}
void XStampWidget::OnLocationChanged(wxCommandEvent& evt)
{
if (!m_config)
{
evt.Skip();
return;
}
m_config->Write("/Stamp/Location", evt.GetSelection());
m_config->Flush();
evt.Skip();
}
void XStampWidget::OnTransparencyToggled(wxCommandEvent& evt)
{
lblBackgroundColour->Show(evt.GetInt() == 0);
cpkBackgroundStamp->Show(evt.GetInt() == 0);
if (GetSizer())
GetSizer()->Layout();
if (!m_config)
{
evt.Skip();
return;
}
m_config->Write("/Stamp/Transparency", evt.GetInt());
m_config->Flush();
evt.Skip();
}
StampDescriptor * XStampWidget::GetStampDescriptor()
{
m_descriptor.reset(nullptr);
m_descriptor = std::make_unique<StampDescriptor> ();
m_descriptor->text = txtStamp->GetValue();
m_descriptor->font = fpkStamp->GetSelectedFont();
m_descriptor->foregroundColour = cpkForegroundStamp->GetColour();
m_descriptor->backgroundColour = cpkBackgroundStamp->GetColour();
m_descriptor->rotationAngle = sldTextRotationAngle->GetValue();
m_descriptor->location = cmbStampLocation->GetSelection();
m_descriptor->transparent = tglTransparent->GetValue();
// Not setting image, it is to be created.
return m_descriptor.get();
}
void XStampWidget::OnBitmapPreview(wxMouseEvent& evt)
{
StampDescriptor * descriptor = GetStampDescriptor();
wxImage preview = StampWorker::CreateStamp(descriptor, 72);
m_sbmpPreview->SetBitmap(wxBitmapBundle(preview));
GetSizer()->Layout();
evt.Skip();
}

View File

@@ -0,0 +1,52 @@
// /*
// * File: XStampWidget.h
// * Author: Saleem Edah-Tally - nmset@yandex.com
// * License : CeCILL-C
// * Copyright Saleem Edah-Tally - © 2025
// *
// * Created on 01 07 2025, 20:35
// */
#ifndef XSTAMPWIDGET_H
#define XSTAMPWIDGET_H
#include "StampWidget.h"
#include <wx/wx.h>
#include <wx/config.h>
#include <wx/generic/statbmpg.h>
struct StampDescriptor;
/**
* This widget collects inputs for the creation of a stamp on scan pages.\n
* - text
* - font
* - foreground colour
* - background colour
* - angle of rotation
* - transparency.
*/
class XStampWidget : public StampWidget
{
DECLARE_DYNAMIC_CLASS( XStampWidget )
public:
XStampWidget( wxWindow* parent, wxWindowID id = SYMBOL_STAMPWIDGET_IDNAME, const wxPoint& pos = SYMBOL_STAMPWIDGET_POSITION, const wxSize& size = SYMBOL_STAMPWIDGET_SIZE, long style = SYMBOL_STAMPWIDGET_STYLE );
bool Setup(wxConfig * config);
StampDescriptor * GetStampDescriptor();
private:
wxConfig * m_config;
std::unique_ptr<StampDescriptor> m_descriptor;
wxGenericStaticBitmap * m_sbmpPreview;
void OnFontChanged(wxFontPickerEvent& evt);
void OnForegroundColourChanged(wxColourPickerEvent& evt);
void OnBackgroundColourChanged(wxColourPickerEvent& evt);
void OnAngleSliderChanged(wxScrollEvent& evt);
void OnTxtKeyPressed ( wxKeyEvent& evt );
void OnLocationChanged(wxCommandEvent& evt);
void OnTransparencyToggled(wxCommandEvent& evt);
void OnBitmapPreview(wxMouseEvent& evt);
};
#endif // XSTAMPWIDGET_H

View File

@@ -0,0 +1,88 @@
// /*
// * File: XStampWidgets.cpp
// * Author: Saleem Edah-Tally - nmset@yandex.com
// * License : CeCILL-C
// * Copyright Saleem Edah-Tally - © 2025
// *
// * Created on 06 07 2025, 22:41
// */
#include "XStampWidgets.h"
#include <XStampWidget.h>
#include <DefsStampWidget.h>
IMPLEMENT_CLASS( XStampWidgets, StampWidgets )
XStampWidgets::XStampWidgets(wxWindow* parent, wxWindowID id, const wxPoint& pos,
const wxSize& size, long style)
: StampWidgets(parent, id, pos, size, style)
{}
bool XStampWidgets::Setup(wxFileConfig* config)
{
m_config = config;
if (!m_config)
return false;
wxMouseEvent evt;
AddStampWidget(evt);
btnAddStampWidget->Bind(wxEVT_LEFT_UP, &XStampWidgets::AddStampWidget, this);
btnDeleteStampWidget->Bind(wxEVT_LEFT_UP, &XStampWidgets::DeleteStampWidget, this);
return true;
}
void XStampWidgets::AddStampWidget(wxMouseEvent& evt)
{
XStampWidget * stampWidget = new XStampWidget(nbStampWidgets);
stampWidget->Setup(m_config);
stampWidget->Bind(wxEVT_COMMAND_COMBOBOX_SELECTED, &XStampWidgets::OnStampLocationChanged, this);
nbStampWidgets->AddPage(stampWidget, stampWidget->cmbStampLocation->GetStringSelection(), true);
if (GetParent()->GetSizer())
GetParent()->GetSizer()->Layout();
evt.Skip();
}
void XStampWidgets::DeleteStampWidget(wxMouseEvent& evt)
{
int selectedPage = nbStampWidgets->GetSelection();
if (selectedPage == wxNOT_FOUND)
{
evt.Skip();
return;
}
nbStampWidgets->DeletePage(selectedPage);
evt.Skip();
}
std::vector<StampDescriptor*> * XStampWidgets::GetStampDescriptors()
{
m_stampDescriptors.clear(); // Stored as unique_ptr in XStampWidget.
for (uint i = 0; i < nbStampWidgets->GetPageCount(); i++)
{
XStampWidget * stampWidget = static_cast<XStampWidget*> (nbStampWidgets->GetPage(i));
if (stampWidget)
{
m_stampDescriptors.push_back(stampWidget->GetStampDescriptor());
}
}
return &m_stampDescriptors;
}
void XStampWidgets::OnStampLocationChanged(wxCommandEvent& evt)
{
int selectedPage = nbStampWidgets->GetSelection();
if (selectedPage == wxNOT_FOUND)
{
evt.Skip();
return;
}
XStampWidget * stampWidget = static_cast<XStampWidget*> (nbStampWidgets->GetPage(selectedPage));
if (!stampWidget)
{
evt.Skip();
return;
}
nbStampWidgets->SetPageText(selectedPage, stampWidget->cmbStampLocation->GetStringSelection());
evt.Skip();
}

View File

@@ -0,0 +1,41 @@
// /*
// * File: XStampWidgets.h
// * Author: Saleem Edah-Tally - nmset@yandex.com
// * License : CeCILL-C
// * Copyright Saleem Edah-Tally - © 2025
// *
// * Created on 06 07 2025, 22:41
// */
#ifndef XSTAMPWIDGETS_H
#define XSTAMPWIDGETS_H
#include "StampWidgets.h"
#include <wx/wx.h>
#include <wx/config.h>
#include <vector>
#include <memory>
struct StampDescriptor;
class XStampWidgets : public StampWidgets
{
DECLARE_DYNAMIC_CLASS( XStampWidgets )
public:
XStampWidgets(wxWindow* parent, wxWindowID id = SYMBOL_STAMPWIDGETS_IDNAME,
const wxPoint& pos = SYMBOL_STAMPWIDGETS_POSITION,
const wxSize& size = SYMBOL_STAMPWIDGETS_SIZE,
long style = SYMBOL_STAMPWIDGETS_STYLE);
bool Setup(wxConfig * config);
std::vector<StampDescriptor*> * GetStampDescriptors();
private:
wxConfig * m_config;
std::vector<StampDescriptor*> m_stampDescriptors;
void AddStampWidget(wxMouseEvent& evt);
void DeleteStampWidget(wxMouseEvent& evt);
void OnStampLocationChanged(wxCommandEvent& evt);
};
#endif // XSTAMPWIDGETS_H

View File

@@ -419,7 +419,7 @@
<bool name="proxy-wxRIGHT">1</bool> <bool name="proxy-wxRIGHT">1</bool>
<bool name="proxy-wxSHAPED">0</bool> <bool name="proxy-wxSHAPED">0</bool>
<bool name="proxy-wxTOP">1</bool> <bool name="proxy-wxTOP">1</bool>
<string name="title">"wxBoxSizer V"</string> <string name="title">"wxBoxSizer V: szMain"</string>
<long name="title-mode">0</long> <long name="title-mode">0</long>
<string name="type">"dialog-control-document"</string> <string name="type">"dialog-control-document"</string>
<document> <document>
@@ -526,7 +526,7 @@ Double-click to go to the selected directory."</string>
<long name="proxy-Height">-1</long> <long name="proxy-Height">-1</long>
<string name="proxy-Help text">"Basename"</string> <string name="proxy-Help text">"Basename"</string>
<bool name="proxy-Hidden">0</bool> <bool name="proxy-Hidden">0</bool>
<string name="proxy-Id name">"ID_TEXTCTRL"</string> <string name="proxy-Id name">"ID_TEXTCTRL_S7"</string>
<long name="proxy-Id value">10003</long> <long name="proxy-Id value">10003</long>
<string name="proxy-Implementation filename">""</string> <string name="proxy-Implementation filename">""</string>
<string name="proxy-Initial value">""</string> <string name="proxy-Initial value">""</string>
@@ -576,7 +576,7 @@ Double-click to go to the selected directory."</string>
<bool name="proxy-wxWANTS_CHARS">0</bool> <bool name="proxy-wxWANTS_CHARS">0</bool>
<long name="proxy-X">-1</long> <long name="proxy-X">-1</long>
<long name="proxy-Y">-1</long> <long name="proxy-Y">-1</long>
<string name="title">"wxTextCtrl: ID_TEXTCTRL"</string> <string name="title">"wxTextCtrl: ID_TEXTCTRL_S7"</string>
<long name="title-mode">0</long> <long name="title-mode">0</long>
<string name="type">"dialog-control-document"</string> <string name="type">"dialog-control-document"</string>
</document> </document>

View File

@@ -126,7 +126,7 @@ void S7::CreateControls()
dpkDestination->SetToolTip(_("Select a destination directory.\nDouble-click to go to the selected directory.")); dpkDestination->SetToolTip(_("Select a destination directory.\nDouble-click to go to the selected directory."));
szMain->Add(dpkDestination, 0, wxGROW|wxALL, 5); szMain->Add(dpkDestination, 0, wxGROW|wxALL, 5);
txtBasename = new wxTextCtrl( panMain, ID_TEXTCTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); txtBasename = new wxTextCtrl( panMain, ID_TEXTCTRL_S7, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
txtBasename->SetHelpText(_("Basename")); txtBasename->SetHelpText(_("Basename"));
if (S7::ShowToolTips()) if (S7::ShowToolTips())
txtBasename->SetToolTip(_("Specify a destination file basename (without extension).\n\n'CTRL + click' for about information.")); txtBasename->SetToolTip(_("Specify a destination file basename (without extension).\n\n'CTRL + click' for about information."));

View File

@@ -39,7 +39,7 @@ class wxDirPickerCtrl;
#define ID_S7 10000 #define ID_S7 10000
#define ID_PANEL 10001 #define ID_PANEL 10001
#define ID_DIRPICKERCTRL 10002 #define ID_DIRPICKERCTRL 10002
#define ID_TEXTCTRL 10003 #define ID_TEXTCTRL_S7 10003
#define SYMBOL_S7_STYLE wxCAPTION|wxRESIZE_BORDER|wxSYSTEM_MENU|wxCLOSE_BOX #define SYMBOL_S7_STYLE wxCAPTION|wxRESIZE_BORDER|wxSYSTEM_MENU|wxCLOSE_BOX
#define SYMBOL_S7_TITLE _("S7") #define SYMBOL_S7_TITLE _("S7")
#define SYMBOL_S7_IDNAME ID_S7 #define SYMBOL_S7_IDNAME ID_S7

View File

@@ -17,6 +17,8 @@
using namespace std; using namespace std;
IMPLEMENT_CLASS( XS7, S7 )
XS7::XS7(wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style) XS7::XS7(wxWindow* parent, wxWindowID id, const wxString& caption, const wxPoint& pos, const wxSize& size, long style)
: S7(parent, id, caption, pos, size, style) : S7(parent, id, caption, pos, size, style)
{} {}
@@ -63,7 +65,8 @@ void XS7::Setup()
SetStatusBar(sb); SetStatusBar(sb);
m_insaneWidget = new XInsaneWidget(panMain, sb, m_config.get()); m_insaneWidget = new XInsaneWidget(panMain, sb, m_config.get());
szMain->Add(m_insaneWidget, 0, wxGROW | wxALL); m_insaneWidget->Setup();
szMain->Insert(2, m_insaneWidget, 1, wxGROW | wxALL);
dpkDestination->Bind ( wxEVT_DIRPICKER_CHANGED, &XS7::OnDpkRepositoryChange, this ); dpkDestination->Bind ( wxEVT_DIRPICKER_CHANGED, &XS7::OnDpkRepositoryChange, this );
dpkDestination->GetTextCtrl()->Bind ( wxEVT_LEFT_DCLICK, &XS7::OnDpkDoubleClick, this ); dpkDestination->GetTextCtrl()->Bind ( wxEVT_LEFT_DCLICK, &XS7::OnDpkDoubleClick, this );

1
XS7.h
View File

@@ -17,6 +17,7 @@
class XS7 : public S7 class XS7 : public S7
{ {
DECLARE_DYNAMIC_CLASS( XS7 )
public: public:
XS7(wxWindow* parent, wxWindowID id = SYMBOL_S7_IDNAME, const wxString& caption = SYMBOL_S7_TITLE, XS7(wxWindow* parent, wxWindowID id = SYMBOL_S7_IDNAME, const wxString& caption = SYMBOL_S7_TITLE,
const wxPoint& pos = SYMBOL_S7_POSITION, const wxSize& size = SYMBOL_S7_SIZE, long style = SYMBOL_S7_STYLE ); const wxPoint& pos = SYMBOL_S7_POSITION, const wxSize& size = SYMBOL_S7_SIZE, long style = SYMBOL_S7_STYLE );

View File

@@ -11,7 +11,7 @@
#define GLOBALS_H #define GLOBALS_H
#define _APPNAME_ "S7" #define _APPNAME_ "S7"
#define _APPVERSION_ 1 #define _APPVERSION_ 2
#define _INSANEWIDGET_NAME "InsaneWidget" #define _INSANEWIDGET_NAME "InsaneWidget"
#endif /* GLOBALS_H */ #endif /* GLOBALS_H */