/* GIMP - The GNU Image Manipulation Program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * WIA Scanner plug-in * * Copyright (C) 2026 Alex S. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include /* Needed when compiling with gcc */ #include #include #include #include #include #include "libgimp/gimp.h" #include "libgimp/stdplugins-intl.h" #define PLUG_IN_NAME "wia-acquire" #define PLUG_IN_DESCRIPTION _("Capture an image from a WIA datasource") #define PLUG_IN_HELP N_("This plug-in will capture an image from a WIA datasource") #define MAX_FOLDER_PATH 1024 typedef struct _Wia Wia; typedef struct _WiaClass WiaClass; struct _Wia { GimpPlugIn parent_instance; }; struct _WiaClass { GimpPlugInClass parent_class; }; #define WIA_TYPE (wia_get_type ()) #define WIA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WIA_TYPE, Wia)) GType wia_get_type (void) G_GNUC_CONST; static GList * wia_query_procedures (GimpPlugIn *plug_in); static GimpProcedure * wia_create_procedure (GimpPlugIn *plug_in, const gchar *name); static GimpValueArray * wia_run (GimpProcedure *procedure, GimpRunMode run_mode, GimpImage *image, GimpDrawable **drawables, GimpProcedureConfig *config, gpointer run_data); static GimpValueArray * wia_main (GimpProcedure *procedure, GimpProcedureConfig *config); static HRESULT CreateWiaDeviceManager (IWiaDevMgr2 **ppWiaDevMgr); G_DEFINE_TYPE (Wia, wia, GIMP_TYPE_PLUG_IN) GIMP_MAIN (WIA_TYPE) DEFINE_STD_SET_I18N static void wia_class_init (WiaClass *klass) { GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass); plug_in_class->query_procedures = wia_query_procedures; plug_in_class->create_procedure = wia_create_procedure; plug_in_class->set_i18n = STD_SET_I18N; } static void wia_init (Wia *wia) { } static GList * wia_query_procedures (GimpPlugIn *plug_in) { return g_list_append (NULL, g_strdup (PLUG_IN_NAME)); } static GimpProcedure * wia_create_procedure (GimpPlugIn *plug_in, const gchar *name) { GimpProcedure *procedure = NULL; if (! strcmp (name, PLUG_IN_NAME)) { procedure = gimp_image_procedure_new (plug_in, name, GIMP_PDB_PROC_TYPE_PLUGIN, wia_run, NULL, NULL); gimp_procedure_set_image_types (procedure, "*"); gimp_procedure_set_sensitivity_mask (procedure, GIMP_PROCEDURE_SENSITIVE_DRAWABLE | GIMP_PROCEDURE_SENSITIVE_DRAWABLES | GIMP_PROCEDURE_SENSITIVE_NO_DRAWABLES | GIMP_PROCEDURE_SENSITIVE_NO_IMAGE); gimp_procedure_set_menu_label (procedure, _("_Scanner/Camera...")); gimp_procedure_add_menu_path (procedure, "/File/Create"); gimp_procedure_set_documentation (procedure, PLUG_IN_DESCRIPTION, PLUG_IN_HELP, name); gimp_procedure_set_attribution (procedure, "Alx Sa", "Alx Sa", "2026"); gimp_procedure_add_string_aux_argument (procedure, "scan-directory", NULL, NULL, NULL, GIMP_PARAM_READWRITE); gimp_procedure_add_core_object_array_return_value (procedure, "images", "Array of acquired images", "Array of acquired images", GIMP_TYPE_IMAGE, G_PARAM_READWRITE); } return procedure; } static GimpValueArray * wia_run (GimpProcedure *procedure, GimpRunMode run_mode, GimpImage *image, GimpDrawable **drawables, GimpProcedureConfig *config, gpointer run_data) { gegl_init (NULL, NULL); if (run_mode == GIMP_RUN_NONINTERACTIVE) /* Currently, we don't do non-interactive calls. * Bail if someone tries to call us non-interactively */ return gimp_procedure_new_return_values (procedure, GIMP_PDB_CALLING_ERROR, NULL); return wia_main (procedure, config); } /* Windows methods */ static HRESULT CreateWiaDeviceManager (IWiaDevMgr2 **ppWiaDevMgr) { HRESULT hr; if (NULL == ppWiaDevMgr) return E_INVALIDARG; *ppWiaDevMgr = NULL; hr = CoCreateInstance (&CLSID_WiaDevMgr2, NULL, CLSCTX_LOCAL_SERVER, &IID_IWiaDevMgr2, (void**) ppWiaDevMgr); return hr; } static GimpValueArray * wia_main (GimpProcedure *procedure, GimpProcedureConfig *config) { GError *error = NULL; HRESULT hr = CoInitialize (NULL); if (SUCCEEDED (hr)) { IWiaDevMgr2 *pWiaDevMgr2 = NULL; hr = CreateWiaDeviceManager (&pWiaDevMgr2); if (SUCCEEDED (hr)) { GimpImage **images = NULL; GimpValueArray *return_vals = NULL; HWND window_handle; LONG nFiles = 0; LPWSTR *rFilePaths = NULL; IWiaItem2 *pItem = NULL; BSTR szFolder = NULL; BSTR szFileName; TCHAR temp_path[MAX_FOLDER_PATH]; gchar *scan_directory = NULL; szFileName = SysAllocString (L"GIMP-SCANNED-IMAGE"); /* Check if we have a saved directory, and if so, if it's still * valid. */ g_object_get (config, "scan-directory", &scan_directory, NULL); if (scan_directory != NULL) { GDir *directory = g_dir_open (scan_directory, 0, NULL); if (directory) { szFolder = SysAllocString (g_utf8_to_utf16 (scan_directory, -1, NULL, NULL, NULL)); g_dir_close (directory); } g_free (scan_directory); } /* Otherwise, use Window's temp location */ if (szFolder == NULL) { GetTempPath (MAX_FOLDER_PATH, temp_path); szFolder = SysAllocString (g_utf8_to_utf16 (temp_path, -1, NULL, NULL, NULL)); } /* Values taken from app/gui/gui-unique.h */ window_handle = FindWindowW (L"GimpWin32UniqueHandler", L"GimpProxy"); hr = pWiaDevMgr2->lpVtbl->GetImageDlg (pWiaDevMgr2, 0, NULL, window_handle, szFolder, szFileName, &nFiles, &rFilePaths, &pItem); SysFreeString (szFolder); SysFreeString (szFileName); if (FAILED (hr)) { pWiaDevMgr2->lpVtbl->Release (pWiaDevMgr2); pWiaDevMgr2 = NULL; CoUninitialize (); g_set_error (&error, GIMP_PLUG_IN_ERROR, 0, _("Unable to read scanned images.")); return gimp_procedure_new_return_values (procedure, GIMP_PDB_EXECUTION_ERROR, NULL); } if (nFiles > 0) { gboolean has_directory = FALSE; images = g_new0 (GimpImage *, nFiles + 1); for (gint i = 0; i < nFiles; i++) { gchar *filename = g_utf16_to_utf8 (rFilePaths[i], -1, NULL, NULL, NULL); if (filename) { GFile *file = g_file_new_for_path (filename); images[i] = gimp_file_load (GIMP_RUN_NONINTERACTIVE, file); gimp_display_new (images[i]); if (! gimp_image_undo_is_enabled (images[i])) gimp_image_undo_enable (images[i]); /* Store first valid directory for later use */ if (! has_directory) { GFile *parent = g_file_get_parent (file); g_object_set (config, "scan-directory", g_file_get_parse_name (parent), NULL); g_object_unref (parent); has_directory = TRUE; } g_object_unref (file); g_free (filename); } SysFreeString (rFilePaths[i]); } CoTaskMemFree (rFilePaths); } pWiaDevMgr2->lpVtbl->Release (pWiaDevMgr2); pWiaDevMgr2 = NULL; CoUninitialize (); return_vals = gimp_procedure_new_return_values (procedure, GIMP_PDB_SUCCESS, NULL); GIMP_VALUES_TAKE_CORE_OBJECT_ARRAY (return_vals, 1, images); return return_vals; } else { g_set_error (&error, GIMP_PLUG_IN_ERROR, 0, _("WIA driver not found, scanning not available")); return gimp_procedure_new_return_values (procedure, GIMP_PDB_EXECUTION_ERROR, NULL); } CoUninitialize (); } return gimp_procedure_new_return_values (procedure, GIMP_PDB_EXECUTION_ERROR, NULL); }