Skip to content

Instantly share code, notes, and snippets.

@Justasic
Last active July 6, 2018 11:58
Show Gist options
  • Save Justasic/e64356ae9c7de8ad2facd1cf383015fc to your computer and use it in GitHub Desktop.
Save Justasic/e64356ae9c7de8ad2facd1cf383015fc to your computer and use it in GitHub Desktop.
The same as RapidCheckouts.py but written in pure C++ to remove the python dependencies (makes it more portable)
/* MIT License
*
* Copyright (c) 2018 Justin Crawford <[email protected]>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// Compile with the following line:
// x86_64-w64-mingw32-g++ --std=c++17 -Wall -Wextra -Wno-unused-parameter -mwindows -static -g -o RapidCheckouts.exe RapidCheckouts.cpp -lWinspool -lGdi32 -lUser32
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <cstring>
#include <strings.h>
#include <unistd.h>
// #define VC_EXTRALEAN
//#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <Richedit.h>
#include <commctrl.h>
// Globals
HINSTANCE hInst;
// Function Prototypes
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
inline COLORREF CreateHexColor(int hex)
{
int red = ((hex >> 16) & 0xFF) / 1.0;
int green = ((hex >> 8) & 0xFF) / 1.0;
int blue = ((hex)& 0xFF) / 1.0;
if (red < 256 && green < 256 && blue < 256)
return RGB(red, green, blue);
return 0;
}
inline HBRUSH CreateHexBrush(int hex)
{
int red = ((hex >> 16) & 0xFF) / 1.0;
int green = ((hex >> 8) & 0xFF) / 1.0;
int blue = ((hex)& 0xFF) / 1.0;
if (red < 256 && green < 256 && blue < 256)
return CreateSolidBrush(RGB(red, green, blue));
return NULL;
}
inline std::string LastError()
{
char errbuf[513];
DWORD err = GetLastError();
if (!err)
return "No error";
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, errbuf, 512, NULL);
return errbuf;
}
typedef enum
{
PORTRATE = 1,
LANDSCAPE
} Orientations_t;
std::map<std::string, std::map<std::string, std::string>> BuildDict()
{
PRINTER_INFO_1 *pPrinters = nullptr;
DWORD unused = 0;
DWORD sizereq = 0;
DWORD objects = 0;
// First request the size of buffer we need to allocate
EnumPrinters(PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL, nullptr, 1, reinterpret_cast<LPBYTE>(pPrinters), 0, &sizereq, &unused);
// Allocate a 2d array from size required.
pPrinters = reinterpret_cast<PRINTER_INFO_1*>(malloc(sizereq));
// Now actually get the list of printers
EnumPrinters(PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL, nullptr, 1, reinterpret_cast<LPBYTE>(pPrinters), sizereq, &unused, &objects);
// Enumerate the printers
std::map<std::string, std::map<std::string, std::string>> ret;
for (DWORD i = 0; i < objects; ++i)
{
std::string name = pPrinters[i].pName;
ret[name]["flags"] = std::to_string(pPrinters[i].Flags);
ret[name]["description"] = pPrinters[i].pDescription;
ret[name]["comment"] = pPrinters[i].pComment;
}
return ret;
}
class Document
{
HANDLE pHandle;
HDC hPrinter;
HPEN pen;
HFONT font;
Orientations_t orient;
std::string PrinterName;
int scale_factor;
public:
Document(std::string Printer, Orientations_t o = PORTRATE, size_t sf = 20) : orient(o), PrinterName(Printer), scale_factor(sf)
{
}
~Document()
{
this->end_document();
DeleteObject(this->pen);
DeleteObject(this->font);
}
void begin_document()
{
DWORD needed = 0;
printf("Using printer \"%s\"\n", this->PrinterName.c_str());
char buffer[2048];
memset(buffer, 0, 2048);
strcpy(buffer, this->PrinterName.c_str());
// Open our printer
if (!OpenPrinter(buffer, &pHandle, 0))
printf("Error: OpenPrinter failed: %s\n", LastError().c_str());
// Get the DEVMODE pointer
GetPrinter(pHandle, 8, nullptr, 0, &needed);
PRINTER_INFO_8 *pInfo = reinterpret_cast<PRINTER_INFO_8*>(malloc(needed));
if (!GetPrinter(pHandle, 8, reinterpret_cast<LPBYTE>(pInfo), needed, &needed))
printf("Error: Failed to call GetPrinter: %s\n", LastError().c_str());
DEVMODE *devmode = nullptr;
if (pInfo->pDevMode)
devmode = pInfo->pDevMode;
else
printf("Warning: Failed to set paper type and orientation. This likely is due to a driver error or crash and the printer has become unavailable. Please retry this print job again if it does not work\n");
// Set paper size and orientation.
devmode->dmPaperSize = DMPAPER_ENV_10; // Envelope #10
devmode->dmOrientation = this->orient;
// Get Device Context
this->hPrinter = CreateDC("WINSPOOL", this->PrinterName.c_str(), nullptr, devmode);
// Set to hundredths of an inch
SetMapMode(this->hPrinter, MM_TWIPS);
// Start the document.
DOCINFO docinfo;
memset(&docinfo, 0, sizeof(DOCINFO));
docinfo.lpszDocName = "Rapid Checkouts"; // Print job name
docinfo.cbSize = sizeof(DOCINFO);
StartDoc(this->hPrinter, &docinfo);
// Finally, create a pen to draw with
pen = CreatePen(0, (int)this->scale_factor, 0);
SelectObject(this->hPrinter, this->pen);
free(pInfo);
}
void end_document()
{
EndDoc(this->hPrinter);
}
void end_page()
{
EndPage(this->hPrinter);
}
void setfont(const std::string &Name, size_t size, bool bold = true)
{
this->font = CreateFont(size * this->scale_factor, 0, 0, 0, bold ? 700 : 400, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT("Arial"));
SelectObject(this->hPrinter, this->font);
}
void text(size_t x, size_t y, const std::string &value)
{
TextOut(this->hPrinter, this->scale_factor * x, -1 * this->scale_factor * y, value.c_str(), value.size());
}
};
std::vector<std::string> split(std::string str, std::string token)
{
std::vector<std::string> result;
while (str.size())
{
size_t index = str.find(token);
if (index != std::string::npos)
{
result.push_back(str.substr(0, index));
str = str.substr(index + token.size());
if (str.size() == 0)
result.push_back(str);
}
else
{
result.push_back(str);
str = "";
}
}
return result;
}
#if 1
int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpSzArgument, int nFunsterStil)
{
HWND hwnd;
MSG messages;
WNDCLASSEX wincl;
wincl.hInstance = hInst = hThisInstance;
wincl.lpszClassName = "HIE Rapid Checkouts";
wincl.lpfnWndProc = WindowProcedure;
wincl.style = CS_DBLCLKS | CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
wincl.cbSize = sizeof(WNDCLASSEX);
wincl.hIcon = LoadIcon(NULL, IDI_QUESTION);
wincl.hIconSm = LoadIcon(NULL, IDI_QUESTION);
wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = CreateHexBrush(0xF0F0F0);
if (!RegisterClassEx(&wincl))
return 0;
hwnd = CreateWindowEx(
0, // Extended possibilities for variation
wincl.lpszClassName, // Class name
"Holiday Inn Express: Rapid Checkout Printer", // Window title
(WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX),
CW_USEDEFAULT, // Window decides position
CW_USEDEFAULT, // where the window ends up on the screen
500, // Width
500, // Height
HWND_DESKTOP, // The window is a child-window of desktop
NULL, // No Menu
hThisInstance,
NULL // No Creation data
);
ShowWindow(hwnd, nFunsterStil);
while (GetMessage(&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return messages.wParam;
}
char Themes[2][30] =
{
"HIE Green",
"Code Blue"
};
// WIndow procedure for handling window drawing and events
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lparam)
{
static char *strbuf = reinterpret_cast<char *>(malloc((1 << 16))), *sprintfbuf = reinterpret_cast<char *>(malloc((1 << 16)));
switch (message)
{
case WM_CREATE:
{
LOGFONT lf;
HFONT hMyFont;
auto pPrinters = BuildDict();
DWORD szPrinterList = 42 * pPrinters.size();
memset(&lf, 0, sizeof(LOGFONT));
lf.lfWeight = FW_NORMAL;
lf.lfHeight = 15;
strcpy(lf.lfFaceName, "Arial");
hMyFont = CreateFontIndirect(&lf);
// Draw our buttons (we have 2 for now)
CreateWindowEx(WS_EX_CLIENTEDGE, "button", "Print", WS_VISIBLE | WS_CHILD, 350, 450, 65, 20, hwnd, (HMENU)1, NULL, NULL);
CreateWindowEx(WS_EX_CLIENTEDGE, "button", "Leave", WS_VISIBLE | WS_CHILD, 420, 450, 65, 20, hwnd, (HMENU)2, NULL, NULL);
// Draw our position fields, x axis and y axis
CreateWindowEx(WS_EX_CLIENTEDGE, "Edit", "X" /* X axis*/, WS_VISIBLE | WS_CHILD | WS_BORDER, 400, 20, 80, 20, hwnd, (HMENU)3, NULL, NULL);
CreateWindowEx(WS_EX_CLIENTEDGE, "Edit", "Y" /* Y axis*/, WS_VISIBLE | WS_CHILD | WS_BORDER, 300, 20, 80, 20, hwnd, (HMENU)4, NULL, NULL);
// Apparently this is needed for the rich edit?
LoadLibrary("Msftedit.dll");
// Add the rich edit for room numbers
CreateWindowExW(WS_EX_CLIENTEDGE, MSFTEDIT_CLASS, L"", ES_MULTILINE | WS_VISIBLE | WS_CHILD | WS_BORDER | WS_TABSTOP, 10, 45, 470, 390, hwnd, (HMENU)5, NULL, NULL);
// Add the combo box, we need to grab it's window object to add items to the list
HWND tcmbo = CreateWindowEx(WS_EX_CLIENTEDGE, WC_COMBOBOX, "", CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE, 90, 15, 100, 100, hwnd, (HMENU)6, NULL, NULL);
HWND pcmbo = CreateWindowEx(WS_EX_CLIENTEDGE, WC_COMBOBOX, "", CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE, 10, 445, 300, szPrinterList, hwnd, (HMENU)7, NULL, NULL);
// Add our labels for stuff
CreateWindowEx(0, "STATIC", "Text position from top left", WS_VISIBLE | WS_CHILD, 315, 5, 140, 15, hwnd, (HMENU)8, NULL, NULL);
CreateWindowEx(0, "STATIC", "Folder Style: ", WS_VISIBLE | WS_CHILD, 10, 20, 80, 15, hwnd, (HMENU)9, NULL, NULL);
CreateWindowEx(0, "STATIC", "X: ", WS_VISIBLE | WS_CHILD, 385, 22, 12, 15, hwnd, (HMENU)10, NULL, NULL);
CreateWindowEx(0, "STATIC", "Y: ", WS_VISIBLE | WS_CHILD, 285, 22, 12, 15, hwnd, (HMENU)11, NULL, NULL);
// Set char limits to our X,Y coorid boxes of 5 characters
SendMessage(GetDlgItem(hwnd, 3), static_cast<UINT>(EM_LIMITTEXT), static_cast<WPARAM>(5), static_cast<LPARAM>(0));
SendMessage(GetDlgItem(hwnd, 4), static_cast<UINT>(EM_LIMITTEXT), static_cast<WPARAM>(5), static_cast<LPARAM>(0));
// Add our list items to the theme combo box
for (unsigned i = 0; i < (sizeof(Themes) / 30); ++i)
SendMessage(tcmbo, static_cast<UINT>(CB_ADDSTRING), static_cast<WPARAM>(0), reinterpret_cast<LPARAM>(Themes[i]));
// Select the default theme (Code Blue for now)
SendMessage(tcmbo, CB_SETCURSEL, static_cast<WPARAM>(1), static_cast<LPARAM>(0));
// Send all the text for the printer list to the combo box
for (const auto &i : pPrinters)
SendMessage(pcmbo, static_cast<UINT>(CB_ADDSTRING), static_cast<WPARAM>(0), reinterpret_cast<LPARAM>(i.first.c_str()));
// Again, select the default printer (system default)
{
char buffer[2048];
int i = 0;
DWORD microsoftism = sizeof(buffer);
GetDefaultPrinterA(buffer, &microsoftism);
for (const auto &it : pPrinters)
{
if (it.first == buffer)
SendMessage(pcmbo, CB_SETCURSEL, static_cast<WPARAM>(i), static_cast<LPARAM>(0));
i++;
}
}
// Create the control fonts since the standard one sucks
for (int i = 1; i < 10 + 1; ++i)
SendMessage(GetDlgItem(hwnd, i), WM_SETFONT, (WPARAM)hMyFont, 0);
break;
}
case WM_COMMAND:
// Print button
if (LOWORD(wParam) == 1)
{
//HWND output = GetDlgItem(hwnd, 4);
//HWND input = GetDlgItem(hwnd, 3);
// Get the positions
char Xbuffer[5], Ybuffer[5], buffer[2048];
// Get X,Y coorids
GetDlgItemTextA(hwnd, 3, Xbuffer, sizeof(Xbuffer));
GetDlgItemTextA(hwnd, 4, Ybuffer, sizeof(Ybuffer));
// Get selected printer
GetDlgItemTextA(hwnd, 7, buffer, sizeof(buffer));
// Text -> Integer
int x = atoi(Xbuffer), y = atoi(Ybuffer);
std::string SelectedPrinter = buffer, text;
// Get the values from the RichEdit
size_t szRichText = GetWindowTextLength(GetDlgItem(hwnd, 5));
text.resize(szRichText);
GetDlgItemTextA(hwnd, 5, &text.front(), text.size());
// Now it gets fun. Create the print job.
// Tokenize to a vector of strings for rooms
std::vector<std::string> rooms = split(text, "\n");
snprintf(sprintfbuf, 1 << 16, "%zu Envelopes required\n", rooms.size());
// Create our document
Document doc(SelectedPrinter);
doc.begin_document();
// Set teh font to Arial (what is on the Rapid Checkouts for holiday inn express)
doc.setfont("Arial", 32, true);
// Iterate the room list, create a page for each receipt, then write the room number on it
for (const auto &room : rooms)
{
// The exact position of the "Room#" box for the numbers
doc.text(x, y, room.c_str());
doc.end_page();
}
// Send the document to the print spooler to be dispatched to the selected printer
doc.end_document();
MessageBoxA(hwnd, sprintfbuf, "Rapid Checkouts", MB_OK | MB_ICONINFORMATION);
}
// They changed the combo box option for the Theme
if (LOWORD(wParam) == 6)
{
char buffer[40];
GetDlgItemTextA(hwnd, 6, buffer, sizeof(buffer));
if (!strcmp(buffer, Themes[0]))
{
// Green theme
SetDlgItemTextA(hwnd, 3, "195");
SetDlgItemTextA(hwnd, 4, "597");
}
if (!strcmp(buffer, Themes[1]))
{
// Code blue
}
}
// Quit/Leave/Exit button
if (LOWORD(wParam) == 2)
PostQuitMessage(0);
break;
case WM_DESTROY:
case WM_CLOSE:
free(sprintfbuf);
free(strbuf);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lparam);
}
return 0;
}
#else
int main(int argc, char **argv)
{
// Get the default printer
char buffer[2048];
DWORD microsoftism = sizeof(buffer);
GetDefaultPrinterA(buffer, &microsoftism);
auto printers = BuildDict();
int i = 0;
int dflt = 0;
for (const auto &p : printers)
{
printf("[%d] %s -- %s\n", i, p.first.c_str(), p.second.at("description").c_str());
if (p.first == buffer)
dflt = i;
i++;
}
printf("Enter printer number ([%d] default): ", dflt);
int selection;
std::cin >> selection;
// Find their selection
std::string SelectedPrinter;
i = 0;
for (const auto &p : printers)
{
if (i == selection)
SelectedPrinter = p.first;
i++;
}
if (SelectedPrinter.empty())
{
printf("invalid selection, exiting\n");
return EXIT_FAILURE;
}
printf("%s selected.\n", SelectedPrinter.c_str());
printf("Enter Room List (comma deliminated, use dashes for range): ");
std::string values;
std::cin >> values;
// Tokenize to a vector of strings for rooms
std::vector<std::string> rooms = split(values, ",");
printf("\n\n%zu Envelopes required\n", rooms.size());
// Create our document
Document doc(SelectedPrinter);
doc.begin_document();
// Set teh font to Arial (what is on the Rapid Checkouts for holiday inn express)
doc.setfont("Arial", 32, true);
// Iterate the room list, create a page for each receipt, then write the room number on it
for (const auto &room : rooms)
{
// The exact position of the "Room#" box for the numbers
doc.text(195, 597, room.c_str());
doc.end_page();
}
// Send the document to the print spooler to be dispatched to the selected printer
doc.end_document();
printf("Press any key to exit.\n");
std::getchar();
return EXIT_SUCCESS;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment