TWAIN跨平台基础代码

This commit is contained in:
gb 2023-07-10 15:28:45 +08:00
parent 1c30e8bf3a
commit d966939524
16 changed files with 10757 additions and 27 deletions

View File

@ -4,6 +4,8 @@
#include <stdio.h>
#include <string>
#include <stdarg.h>
#include <huagao/brand.h>
#if defined(WIN32) || defined(_WIN64)
#include <Windows.h>
#include <direct.h>
@ -618,6 +620,108 @@ extern "C"
return lv;
}
std::string get_command_result(const char* cmd, int len)
{
std::string result("");
#if OS_WIN
#else
FILE* src = popen(cmd, "r");
if (src)
{
char buf[128] = { 0 };
int rv = fread(buf, 1, sizeof(buf) - 1, src);
while (rv > 0)
{
buf[rv] = 0;
result += buf;
if (len != -1 && result.length() >= len)
{
result.erase(len);
break;
}
rv = fread(buf, 1, sizeof(buf) - 1, src);
}
pclose(src);
}
#endif
return std::move(result);
}
const char* trim(std::string& str, const char* sp = " \r\n\t")
{
int pos = 0;
char ch[2] = { 0 };
for (; pos < str.length(); ++pos)
{
ch[0] = str[pos];
if (!strstr(sp, ch))
break;
}
if (pos)
str.erase(0, pos);
pos = str.length() - 1;
for (; pos >= 0; --pos)
{
ch[0] = str[pos];
if (!strstr(sp, ch))
break;
}
if (++pos < str.length())
str.erase(pos);
return str.c_str();
}
std::string get_local_data_path(void)
{
static std::string ldp("");
if (ldp.empty())
{
#if OS_WIN
const char* path(getenv("LOCALAPPDATA"));
if (path)
{
ldp = path;
ldp += PATH_SEPARATOR;
}
#else
const char* path(getenv("HOME"));
if (path)
{
if (strstr(path, "/root"))
{
std::string usr(get_command_result("logname", -1));
ldp = std::string("/home/") + trim(usr);
if (!opendir(ldp.c_str()))
{
printf("opendir(%s) failed: %s\n", ldp.c_str(), strerror(errno));
ldp = path;
}
}
else
{
ldp = path;
}
ldp += std::string(PATH_SEPARATOR) + ".";
}
#endif
ldp += PRODUCT_VENDOR;
ldp += "Scan";
create_folder(ldp.c_str());
printf("# local data path: %s\n", ldp.c_str());
}
return ldp;
}
std::string local_data_path(void)
{
static std::string local_data_path("");
@ -640,35 +744,10 @@ extern "C"
if (home.empty())
{
#if defined(WIN32) || defined(_WIN64)
std::string env("LOCALAPPDATA"), lead("");
#else
std::string env("HOME"), lead(".");
#endif
char* tmp = getenv(env.c_str());
if (tmp)
home = tmp;
home += PATH_SEPARATOR + lead;
local_data_path = home = get_local_data_path();
}
#ifdef OEM_HANWANG
home += "HanvonScan";
#elif defined(OEM_LISICHENG)
home += "LanxumScan";
#elif defined(OEM_CANGTIAN)
home += "CumTennScan";
#elif defined(OEM_ZHONGJING)
home += "MicrotekScan";
#elif defined(OEM_ZIGUANG)
home += "UniScan";
#else
home += "HuaGoScan";
#endif;
create_folder(home.c_str());
return home;
return std::move(home);
}
std::string temporary_path(void)
{

40
twain/CMakeLists.txt Normal file
View File

@ -0,0 +1,40 @@
project(hgtwain)
#add_compile_options(-std=c++11)
add_definitions(-DTWPP_IS_DS)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -fvisibility=hidden")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -fvisibility=hidden")
aux_source_directory(${PROJECT_SOURCE_DIR} DIR_SRCS)
# add_subdirectory(twain)
file(GLOB DIR_HEADS "${PROJECT_SOURCE_DIR}/*.h" "${PROJECT_SOURCE_DIR}/*.hpp" "${PROJECT_SOURCE_DIR}/*.cpp"
"${PROJECT_SOURCE_DIR}/../hgdriver/wrapper/ini_file.h"
"${PROJECT_SOURCE_DIR}/../hgdriver/wrapper/ini_file.cpp")
set(DIR_SRCS ${DIR_SRCS} ${DIR_HEADS})
add_library(${PROJECT_NAME} SHARED ${DIR_SRCS})
link_libraries(dl)
#add_executable(libhgsane.so IMPORTED)
#link_directories(${PROJECT_NAME} PRIVATE
# ${PROJECT_SOURCE_DIR}/sdk/lib
# )
target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/../../sdk/include
${PROJECT_SOURCE_DIR}/../../sdk/include/huagao
${PROJECT_SOURCE_DIR}/../../sdk/include/twain
${PROJECT_SOURCE_DIR}/../../sdk/include/twain/twpp
${PROJECT_SOURCE_DIR}/../hgdriver/wrapper
)
target_link_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/../../release/kylin/x86_64)
target_link_libraries(${PROJECT_NAME} PRIVATE
pthread
usb-1.0
)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../../release/kylin/x86_64)

23
twain/build.sh Normal file
View File

@ -0,0 +1,23 @@
echo off
clear
origin_dir=$(pwd)
echo origin path is $origin_dir
if [ -f ../../build/twain/Makefile ]; then
echo ""
else
mkdir ../../build
mkdir ../../build/twain
fi
cd ../../build/twain
echo building path is $(pwd)
rm -rf ./*
cmake $origin_dir
make -j4
cd $origin_dir
sudo cp ../../release/kylin/x86_64/libhgtwain.so /usr/local/lib/twain/libhgtwain.so.ds

3899
twain/huagaods.cpp Normal file

File diff suppressed because it is too large Load Diff

155
twain/huagaods.hpp Normal file
View File

@ -0,0 +1,155 @@
#ifndef SIMPLEDS_HPP
#define SIMPLEDS_HPP
#include <unordered_map>
#include <vector>
// #include <atlstr.h>
#include <functional>
#include <queue>
#include <string>
#include <thread>
#include "twpp.hpp"
#include "utils.h"
#include "s2t_api.h"
namespace std {
template<>
struct hash<Twpp::CapType> {
size_t operator()(Twpp::CapType cap) const {
return hash<Twpp::UInt16>()(static_cast<Twpp::UInt16>(cap));
}
};
}
enum scanner_status
{
SCANNER_STATUS_NOT_INIT = 0, // has not call identityOpenDs or called identityCloseDs
SCANNER_STATUS_READY, // called identityOpenDs
SCANNER_STATUS_SCAN_1, // scanner_->start() should be called
SCANNER_STATUS_SCANNING, // received SANE_EVENT_WORKING
SCANNER_STATUS_STOPPED, // received SANE_EVENT_SCAN_FINISHED
};
class huagao_ds : public Twpp::SourceFromThis<huagao_ds> {
std::unordered_map<Twpp::CapType, std::function<Twpp::Result(Twpp::CapType type, Twpp::Msg msg, Twpp::Capability& data)>> m_caps;
std::unordered_map<Twpp::CapType, Twpp::MsgSupport> m_query;
std::unordered_map<int, int> org_func_; // <my id, function origin id>
std::unique_ptr <ISaneInvoker, delete_scanner> scanner_;
Twpp::SetupFileXfer m_fileXfer;
Twpp::XferMech m_capXferMech = Twpp::XferMech::Native;
std::unique_ptr<std::thread> memoryinfo;
bool m_memoryfalg = true;
bool m_bFeederEnabled = true;
bool m_bAutoFeed = true;
Twpp::Bool m_autoscan = true;
int automaticcolortype_ = 0;
bool multi_out_ = false;
bool m_bIndicator = true;
int m_jpegQuality = 80;
Twpp::Compression m_compression = Twpp::Compression::None;
SANE_Parameters* cur_head_;
int dpi_;
volatile int scanner_status_;
bool xfer_ready_failed_;
bool log_all_triple_;
bool app_trigger_event_;
bool bUiOnly_;
bool show_setting_;
int count_;
volatile bool notfify_close_ = false;
enum {NOTIFY_AUTO = 0, NOTIFY_ALWAYS, NOTIFY_NONE};
volatile unsigned notify_close_ = 0; // 0 - auto; 1 - notify always; 2 - no notify
enum {DOUBLE_CHECK_TWAIN = 1, DOUBLE_CHECK_ULTRASONIC, DOUBLE_CHECK_INFRARED}; // for BUG-402
volatile unsigned double_check_mode_ = 0; // 1 - standard twain protocol; 2 - bool for ultrasonic; 3 - bool for infrared
std::mutex notify_close_lock_;
std::unique_ptr<std::thread> notify_close_thread_;
bool take_and_reset_notify_close_flag(void);
void notify_close_thread(void);
static std::string get_hidedlg_path(void);
static void showmsg(const char* msg, int err);
static int on_scanner_event(int ev, void* param);
void CapabilityPrintf(Twpp::Msg msg, std::string capability, std::string value = "");
Twpp::Result capCommon(const Twpp::Identity& origin, Twpp::Msg msg, Twpp::Capability& data);
Twpp::Result showTwainUI(Twpp::UserInterface& data, bool bUiOnly = false);
void init_support_caps(void);
void init_caps_from_sane_directly(void);
std::string get_config_file(void);
std::string get_config_value(const char* sec, const char* key);
DWORD get_config_number(const char* sec, const char* key);
int handle_scanner_event(int ev, bool from_event_proc = true);
int get_scanned_image_count(DWORD timeout);
void trigger_ProcessEvent(Twpp::DataGroup dg, Twpp::Dat dat, Twpp::Msg msg);
typedef struct _pending_xfer
{
IScanImg* img = nullptr;
unsigned int off = 0;
void clear(void)
{
if (img)
img->release();
img = NULL;
off = 0;
}
}PENDXFER;
PENDXFER pending_xfer_;
public:
huagao_ds();
virtual ~huagao_ds();
static const Twpp::Identity& defaultIdentity() noexcept;
static Twpp::Result selectIdentity(Twpp::Identity& ident) noexcept;
static Twpp::ConditionCode condition_code_from_hg_error(int hgerr);
// SourceFromThis interface
protected:
typedef Twpp::SourceFromThis<huagao_ds> Base;
virtual Twpp::Result capabilityGet(const Twpp::Identity& origin, Twpp::Capability& data) override;
virtual Twpp::Result capabilityGetCurrent(const Twpp::Identity& origin, Twpp::Capability& data) override;
virtual Twpp::Result capabilityGetDefault(const Twpp::Identity& origin, Twpp::Capability& data) override;
virtual Twpp::Result capabilityQuerySupport(const Twpp::Identity& origin, Twpp::Capability& data) override;
virtual Twpp::Result capabilityReset(const Twpp::Identity& origin, Twpp::Capability& data) override;
virtual Twpp::Result capabilityResetAll(const Twpp::Identity& origin) override;
virtual Twpp::Result capabilitySet(const Twpp::Identity& origin, Twpp::Capability& data) override;
virtual Twpp::Result eventProcess(const Twpp::Identity& origin, Twpp::Event& data) override;
virtual Twpp::Result deviceEventGet(const Twpp::Identity& origin, Twpp::DeviceEvent& data) override;
virtual Twpp::Result identityOpenDs(const Twpp::Identity& origin) override;
virtual Twpp::Result identityCloseDs(const Twpp::Identity& origin) override;
virtual Twpp::Result pendingXfersGet(const Twpp::Identity& origin, Twpp::PendingXfers& data) override;
virtual Twpp::Result pendingXfersEnd(const Twpp::Identity& origin, Twpp::PendingXfers& data) override;
virtual Twpp::Result pendingXfersReset(const Twpp::Identity& origin, Twpp::PendingXfers& data) override;
virtual Twpp::Result setupMemXferGet(const Twpp::Identity& origin, Twpp::SetupMemXfer& data) override;
virtual Twpp::Result userInterfaceDisable(const Twpp::Identity& origin, Twpp::UserInterface& data) override;
virtual Twpp::Result userInterfaceEnable(const Twpp::Identity& origin, Twpp::UserInterface& data) override;
virtual Twpp::Result userInterfaceEnableUiOnly(const Twpp::Identity& origin, Twpp::UserInterface& data) override;
virtual Twpp::Result extImageInfoGet(const Twpp::Identity& origin, Twpp::ExtImageInfo& data) override;
virtual Twpp::Result imageInfoGet(const Twpp::Identity& origin, Twpp::ImageInfo& data) override;
virtual Twpp::Result imageLayoutGet(const Twpp::Identity& origin, Twpp::ImageLayout& data) override;
virtual Twpp::Result imageLayoutGetDefault(const Twpp::Identity& origin, Twpp::ImageLayout& data) override;
virtual Twpp::Result imageLayoutSet(const Twpp::Identity& origin, Twpp::ImageLayout& data) override;
virtual Twpp::Result imageLayoutReset(const Twpp::Identity& origin, Twpp::ImageLayout& data) override;
virtual Twpp::Result imageMemXferGet(const Twpp::Identity& origin, Twpp::ImageMemXfer& data) override;
virtual Twpp::Result imageNativeXferGet(const Twpp::Identity& origin, Twpp::ImageNativeXfer& data) override;
virtual Twpp::Result pendingXfersStopFeeder(const Twpp::Identity& origin, Twpp::PendingXfers& data) override;
virtual Twpp::Result imageFileXferGet(const Twpp::Identity& origin) override;
virtual Twpp::Result setupFileXferGet(const Twpp::Identity& origin, Twpp::SetupFileXfer& data) override;
virtual Twpp::Result setupFileXferGetDefault(const Twpp::Identity& origin, Twpp::SetupFileXfer& data) override;
virtual Twpp::Result setupFileXferSet(const Twpp::Identity& origin, Twpp::SetupFileXfer& data) override;
virtual Twpp::Result setupFileXferReset(const Twpp::Identity& origin, Twpp::SetupFileXfer& data) override;
virtual Twpp::Result call(const Twpp::Identity& origin, Twpp::DataGroup dg, Twpp::Dat dat, Twpp::Msg msg, void* data) override;
virtual Twpp::Result customDataGet(const Twpp::Identity& origin, Twpp::CustomData& data) override;
virtual Twpp::Result customDataSet(const Twpp::Identity& origin, Twpp::CustomData& data) override;
public:
void SetResoluton(const char* path, int resolution);
};
#endif // SIMPLEDS_HPP

445
twain/s2t_api.h Normal file
View File

@ -0,0 +1,445 @@
#pragma once
//
// For: interface definition for SANE to TWAIN
//
// Date: 2022-06-08
//
#include <sane/sane_ex.h>
#include <huagao/hgscanner_error.h>
#include <string>
#include <math.h>
#define COM_API_DECLARE(ret, decl) virtual ret decl = 0
#define COM_API_DECLARE_NON_PURE(ret, decl) virtual ret decl
#define COM_API_OVERRIDE(ret, decl) virtual ret decl override
#define COM_API_IMPLEMENT(cls, ret, decl) ret cls::decl
#define SANE_OPTION_ID_API(opt) COM_API_DECLARE(int, sane_opt_id_##opt(void)) // -1 is none
#define SANE_OPTION_ID_OVERRIDE(opt) COM_API_OVERRIDE(int, sane_opt_id_##opt(void))
#define SANE_OPTION_IMPLEMENT(cls, opt) int cls::sane_opt_id_##opt(void)
#define SANE_OPTION_ID_API_EX(opt) COM_API_DECLARE(int, sane_opt_id_ex_##opt(void)) // -1 is none
#define SANE_OPTION_ID_OVERRIDE_EX(opt) COM_API_OVERRIDE(int, sane_opt_id_ex_##opt(void))
#define SANE_OPTION_IMPLEMENT_EX(cls, opt) int cls::sane_opt_id_ex_##opt(void)
#define ALIGN_MEMORY(n, align) ((n + align - 1) / (align) * (align))
#define _TO_UNICODE_(str) L##str
#define UNICODE_STR(str) _TO_UNICODE_(str)
#define TWPT_AUTOMATIC_COLOR 0x0a0c
#define AUTO_MATIC_ROTATE 123.456f
#define IS_DOUBLE_EQUAL(a, b) fabs((a) - (b)) < .000001
enum value_limit
{
VAL_LIMIT_NONE = 0, //
VAL_LIMIT_ENUM, //
VAL_LIMIT_RANGE, //
VAL_LIMIT_MASK = 0x0ff,
VAL_LIMIT_READONLY = 0x100,
};
enum value_role
{
VAL_ROLE_NONE = 0, // this value is no role but an item of the option only
VAL_ROLE_DEFAULT = 0x01, // this value is the default value of the option
VAL_ROLE_CURRENT = 0x02, // this value is the current value of the option
VAL_ROLE_LOWER = 0x04, // the lower value of a VAL_LIMIT_RANGE
VAL_ROLE_UPPER = 0x08, // the upper value of a VAL_LIMIT_RANGE
VAL_ROLE_STEP = 0x10, // the step value of a VAL_LIMIT_RANGE
};
enum value_type
{
VAL_TYPE_NONE = 0,
VAL_TYPE_BOOL, // bool
VAL_TYPE_INT, // int
VAL_TYPE_FLOAT, // float
VAL_TYPE_STR, // char*
VAL_TYPE_STREAM, // uint8_t[], must give array length
VAL_TYPE_BUTTON, // a button
};
enum color_value // 除最后一项外其余与Twpp::PixelType值保持一致
{
COLOR_BW = 0,
COLOR_GRAY,
COLOR_RGB,
COLOR_AUTO_MATCH = TWPT_AUTOMATIC_COLOR,
};
enum paper_value // 与Twpp::PaperSize保持一致
{
PAPER_A4 = 1,
PAPER_16K = 1,
PAPER_LETTER = 3,
PAPER_LEGAL = 4,
PAPER_A5 = 5,
PAPER_B4 = 6,
PAPER_B6 = 7,
PAPER_A3 = 11,
PAPER_8K = 11,
PAPER_A6 = 13,
PAPER_B5 = 29,
PAPER_STATEMENT = 52, // 匹配原始尺寸
PAPER_MAXSIZE = 54, // 最大扫描尺寸
// 以下为未匹配选项
PAPER_DOUBLE_LETTER = 103,
PAPER_MAXSIZE_CROP = 154, // 最大扫描尺寸自动裁切
PAPER_TRIPPLE = 111, // 三联试卷
};
enum filter_value // 除色选项
{
FILTER_NONE = 0,
FILTER_RED,
FILTER_GREEN,
FILTER_BLUE,
};
enum enhance_value // 颜色增强选项
{
ENHANCE_NONE = 0,
ENHANCE_RED,
ENHANCE_GREEN,
ENHANCE_BLUE,
};
enum sharpen_value
{
SHARPEN_NONE = 0,
SHARPEN_SHARPEN,
SHARPEN_SHARPEN_MORE,
SHARPEN_BLUR,
SHARPEN_BLUR_MORE,
};
enum multiout_value
{
MULTI_OUT_NONE = -1,
MULTI_OUT_ALL,
MULTI_OUT_COLOR_GRAY,
MULTI_OUT_COLOR_BW,
MULTI_OUT_GRAY_BW,
};
enum twain_xfer
{
TWAIN_XFER_Native = 0, // BITMAPINFOHEADER + bits
TWAIN_XFER_File = 1, // BITMAPFILEHEADER + TWAIN_XFER_Native
TWAIN_XFER_Memory = 2, // to be implementing ...
};
typedef bool(* set_opt_value)(void* val, value_role role, value_limit limit, void* param); // return false to stop the callback
class IRef
{
public:
COM_API_DECLARE(long, add_ref(void));
COM_API_DECLARE(long, release(void));
};
class IScanImg : public IRef // 为兼容TWAIN传输方式位图全部转换为完整的位图文件内容TWAIN协议层在获取数据时根据不同传输方式跳过对应的文件头即可 - 2023-07-03
{
public:
COM_API_DECLARE(int, width(void));
COM_API_DECLARE(int, line_bytes(void));
COM_API_DECLARE(int, height(void));
COM_API_DECLARE(int, depth(void));
COM_API_DECLARE(int, channel(void));
COM_API_DECLARE(int, dpi(void));
COM_API_DECLARE(SANE_Frame, type(void));
COM_API_DECLARE(unsigned int, bytes(void));
COM_API_DECLARE(unsigned int, header_size(void));
COM_API_DECLARE(unsigned char*, data(unsigned long long off, unsigned int *bytes));
COM_API_DECLARE(int, read(void* buf, size_t* bytes, unsigned long long off = 0));
COM_API_DECLARE(const char*, file(void));
COM_API_DECLARE(void, keep_file(bool keep));
COM_API_DECLARE(void, copy_header(SANE_Parameters* head));
COM_API_DECLARE(int, image_status(void));
COM_API_DECLARE(size_t, get_bits_offset(void));
};
class ISaneInvoker : public IRef
{
public:
COM_API_DECLARE(int, start(void));
COM_API_DECLARE(int, stop(void));
COM_API_DECLARE(int, get_event(void));
COM_API_DECLARE(void, set_event_callback(int(* handle_ev)(int, void*) = nullptr, void* para = nullptr));
COM_API_DECLARE(bool, wait_image(DWORD milliseconds = -1));
COM_API_DECLARE(int, get_scanned_images(DWORD milliseconds = 0));
COM_API_DECLARE(IScanImg*, take_first_image(twain_xfer xfer = TWAIN_XFER_Native)); // call 'release' on returned value, plz
COM_API_DECLARE(bool, get_first_image_header(SANE_Parameters* header, size_t* bytes = nullptr, int *dpi = nullptr));
COM_API_DECLARE(bool, is_online(void));
COM_API_DECLARE(bool, is_paper_on(void));
COM_API_DECLARE(int, last_error(void));
COM_API_DECLARE(int, image_fetched(IScanImg* tx)); // notify the image 'tx' has fetched by APP
// Function: 获取配置项信息
//
// Parameter: sn - 配置项索引 - sane_option_id
//
// type - 配置项数据类型
//
// limit - 配置项限制类型
//
// bytes - *type 为 VAL_TYPE_STR时需要的最小空间字节数
COM_API_DECLARE(bool, get_option_info(int sn, value_type* type, value_limit* limit, int *bytes));
COM_API_DECLARE(bool, get_value(int sn, set_opt_value, void* param));
COM_API_DECLARE(bool, get_value(int sn, void* data, int* len)); // get operation with in-parameter
COM_API_DECLARE(int, set_value(int sn, void* val));
COM_API_DECLARE(int, convert_image(SANE_ImageFormatConvert* conv));
COM_API_DECLARE(void, free_buffer(void* buf, int len));
COM_API_DECLARE(int, get_fixed_ids(bool(* cb)(uint32_t id, void* param), void* param));
// SANE options ID ...
SANE_OPTION_ID_API(color_correction); // 2023-02-24 15:31:19 色偏校正
SANE_OPTION_ID_API(fold_type); // 2023-02-24 15:28:47 对折模式
SANE_OPTION_ID_API(is_multiout); // 多流输出
SANE_OPTION_ID_API(multiout_type); // 多流输出类型
SANE_OPTION_ID_API(color_mode); // 颜色模式
SANE_OPTION_ID_API(erase_color); // 除色或增强
SANE_OPTION_ID_API(erase_multiout_red); // 多流输出除红
SANE_OPTION_ID_API(erase_paper_red); // 试卷除红
SANE_OPTION_ID_API(is_erase_background); // 背景移除
SANE_OPTION_ID_API(background_color_range); // 背景色彩范围
SANE_OPTION_ID_API(sharpen); // 锐化与模糊
SANE_OPTION_ID_API(erase_morr); // 除摩尔纹
SANE_OPTION_ID_API(erase_grids); // 除网纹
SANE_OPTION_ID_API(error_extend); // 错误扩散
SANE_OPTION_ID_API(is_noise_modify); // 噪点优化
SANE_OPTION_ID_API(noise_threshold); // 噪点优化尺寸
SANE_OPTION_ID_API(paper); // 纸张尺寸
SANE_OPTION_ID_API(is_custom_area); // 自定义扫描区域
SANE_OPTION_ID_API(curstom_area_l); // 自定义扫描区域 左
SANE_OPTION_ID_API(curstom_area_r); // 自定义扫描区域 右
SANE_OPTION_ID_API(curstom_area_t); // 自定义扫描区域 上
SANE_OPTION_ID_API(curstom_area_b); // 自定义扫描区域 下
SANE_OPTION_ID_API(is_size_check); // 尺寸检测
SANE_OPTION_ID_API(page); // 扫描页面
SANE_OPTION_ID_API(blank_page_threshold); // 跳过空白页灵敏度
SANE_OPTION_ID_API(resolution); // 分辨率
SANE_OPTION_ID_API(image_quality); // 图像质量
SANE_OPTION_ID_API(is_swap); // 交换正反面
SANE_OPTION_ID_API(is_split); // 图像拆分
SANE_OPTION_ID_API(is_auto_deskew); // 自动纠偏
SANE_OPTION_ID_API(is_custom_gamma); // 自定义gamma
SANE_OPTION_ID_API(bright); // 亮度
SANE_OPTION_ID_API(contrast); // 对比度
SANE_OPTION_ID_API(gamma); // gamma
SANE_OPTION_ID_API(is_erase_black_frame); // 消除黑框
SANE_OPTION_ID_API(deep_sample); // 深色样张
SANE_OPTION_ID_API(threshold); // 阈值
SANE_OPTION_ID_API(anti_noise); // 背景抗噪等级
SANE_OPTION_ID_API(margin); // 边缘缩进
SANE_OPTION_ID_API(fill_background); // 背景填充方式
SANE_OPTION_ID_API(is_anti_permeate); // 防止渗透
SANE_OPTION_ID_API(anti_permeate_level); // 防止渗透等级
SANE_OPTION_ID_API(is_erase_hole); // 穿孔移除
SANE_OPTION_ID_API(search_hole_range); // 穿孔搜索范围
SANE_OPTION_ID_API(is_filling_color); // 色彩填充
SANE_OPTION_ID_API(is_ultrasonic_check); // 超声波检测
SANE_OPTION_ID_API(is_check_staple); // 装订检测
SANE_OPTION_ID_API(scan_mode); // 扫描张数
SANE_OPTION_ID_API(scan_count); // 扫描数量
SANE_OPTION_ID_API(text_direction); // 文稿方向
SANE_OPTION_ID_API(is_rotate_bkg180); // 背面旋转180度
SANE_OPTION_ID_API(is_check_dogear); // 折角检测
SANE_OPTION_ID_API(dogear_size); // 折角检测大小
SANE_OPTION_ID_API(is_check_skew); // 歪斜检测
SANE_OPTION_ID_API(skew_range); // 歪斜容忍度
SANE_OPTION_ID_API(black_white_threshold); // 二值化图像阈值
SANE_OPTION_ID_API(is_photo_mode); // 照片模式
SANE_OPTION_ID_API(double_feed_handle); // 双张图片处理
SANE_OPTION_ID_API(scan_when_paper_on); // 待纸扫描
SANE_OPTION_ID_API(feed_strength); // 分纸强度
SANE_OPTION_ID_API(power_scheme); // 休眠时间
SANE_OPTION_ID_API(is_auto_strength); // 自动搓纸强度
SANE_OPTION_ID_API(feed_strength_value); // 自动搓纸强度设定值
SANE_OPTION_ID_API(is_reverse_bw); // 黑白图像反色输出
SANE_OPTION_ID_API(is_erase_hole_l); // 穿孔移除 - 左
SANE_OPTION_ID_API(search_hole_range_l); // 穿孔搜索范围 - 左
SANE_OPTION_ID_API(is_erase_hole_r); // 穿孔移除 - 右
SANE_OPTION_ID_API(search_hole_range_r); // 穿孔搜索范围 - 右
SANE_OPTION_ID_API(is_erase_hole_t); // 穿孔移除 - 上
SANE_OPTION_ID_API(search_hole_range_t); // 穿孔搜索范围 - 上
SANE_OPTION_ID_API(is_erase_hole_b); // 穿孔移除 - 下
SANE_OPTION_ID_API(search_hole_range_b); // 穿孔搜索范围 - 下
SANE_OPTION_ID_API(fold_direction); // 对折模式
// SANE-ex option ID:
SANE_OPTION_ID_API_EX(multiout_type); // int
SANE_OPTION_ID_API_EX(auto_color_type); // int
SANE_OPTION_ID_API_EX(color_mode); // int
SANE_OPTION_ID_API_EX(sharpen); // int
SANE_OPTION_ID_API_EX(paper); // paper_value
SANE_OPTION_ID_API_EX(paper_lateral); // bool
SANE_OPTION_ID_API_EX(auto_paper_size); // bool
SANE_OPTION_ID_API_EX(is_paper_auto_crop); // bool
SANE_OPTION_ID_API_EX(text_direction); // float 90, 180, ..., -1 is auto-text-direction
SANE_OPTION_ID_API_EX(duplex); // bool
SANE_OPTION_ID_API_EX(fill_background); // bool true - 凸多边形
SANE_OPTION_ID_API_EX(discard_blank_page); // bool
SANE_OPTION_ID_API_EX(discard_blank_receipt); // bool
SANE_OPTION_ID_API_EX(is_page_fold); // bool
SANE_OPTION_ID_API_EX(color_filter); // int (filter_value)
SANE_OPTION_ID_API_EX(color_enhance); // int (enhance_value)
SANE_OPTION_ID_API_EX(final_compression); // int
SANE_OPTION_ID_API_EX(final_format); // SANE_FinalImgFormat
SANE_OPTION_ID_API_EX(serial); // std::string
SANE_OPTION_ID_API_EX(to_be_scan); // bool
SANE_OPTION_ID_API_EX(scan_with_hole); // bool
SANE_OPTION_ID_API_EX(device_code); // std::string
SANE_OPTION_ID_API_EX(power); // int
SANE_OPTION_ID_API_EX(hardware_version); // std::string
SANE_OPTION_ID_API_EX(ip); // std::string
// ui ...
COM_API_DECLARE(bool, ui_show_main(HWND parent));
COM_API_DECLARE(bool, ui_show_setting(HWND parent, bool with_scan, bool indicator = true));
COM_API_DECLARE(bool, ui_show_progress(HWND parent, bool bIndicator));
COM_API_DECLARE(void, ui_hide(void));
COM_API_DECLARE(bool, ui_is_ok(void));
// twain
COM_API_DECLARE(void, twain_set_transfer(twain_xfer xfer));
COM_API_DECLARE(void, twain_set_compression(SANE_CompressionType compression, void* detail = nullptr));
COM_API_DECLARE(int, twain_get_config(char* buf, size_t* len));
COM_API_DECLARE(int, twain_set_config(char* buf, size_t len));
};
struct delete_scanner
{
void operator()(ISaneInvoker* p)
{
p->release();
}
};
#include <vector>
#include <list>
namespace sane_opts
{
enum
{
RANGE_POS_CURRENT = 0,
RANGE_POS_DEFAULT,
RANGE_POS_ENUM_BEGIN,
RANGE_POS_LOWER = RANGE_POS_ENUM_BEGIN,
RANGE_POS_UPPER,
RANGE_POS_STEP,
};
template<class T>
class get_opts
{
public:
// 0 - cur val; 1 - def val;
//
// LIST: 2 - elements ...
//
// RANGE: 2 - lower; 3 - upper; 4 - step
T range[5];
value_limit lmt_;
std::vector<T>* lvs_;
value_limit* limit_;
public:
get_opts(value_limit* limit, std::vector<T>* ls) : limit_(limit), lvs_(ls)
{}
~get_opts()
{}
void re_order(void)
{
if (limit_)
*limit_ = lmt_;
if (lmt_ == VAL_LIMIT_RANGE || lmt_ == VAL_LIMIT_NONE)
{
if (lvs_)
{
lvs_->clear();
for (size_t i = 0; i < _countof(range); ++i)
lvs_->push_back(range[i]);
}
}
else if(lvs_)
{
lvs_->insert(lvs_->begin(), range[RANGE_POS_DEFAULT]);
lvs_->insert(lvs_->begin(), range[RANGE_POS_CURRENT]);
}
}
};
template<class T>
bool set_opt_value(void* val, value_role role, value_limit limit, void* param)
{
get_opts<T>* v = (get_opts<T>*)param;
bool go = true;
v->lmt_ = limit;
if (role & VAL_ROLE_CURRENT)
{
v->range[RANGE_POS_CURRENT] = *(T*)val;
}
if (role & VAL_ROLE_DEFAULT)
{
v->range[RANGE_POS_DEFAULT] = *(T*)val;
}
if (role & VAL_ROLE_LOWER)
{
v->range[RANGE_POS_LOWER] = *(T*)val;
}
if (role & VAL_ROLE_UPPER)
{
v->range[RANGE_POS_UPPER] = *(T*)val;
}
if (role & VAL_ROLE_STEP)
{
v->range[RANGE_POS_STEP] = *(T*)val;
}
else if (v->lvs_)
v->lvs_->push_back(*(T*)val);
return go;
}
}
#define GET_SANE_OPT(type, object, id_name, limit, vct) \
{ \
sane_opts::get_opts<type> op(limit, vct); \
object->get_value(SANE_OPT_ID_##id_name, sane_opts::set_opt_value<type>, &op); \
op.re_order(); \
}
#define GET_SANE_OPT_EX(type, object, id_name, limit, vct) \
{ \
sane_opts::get_opts<type> op(limit, vct); \
object->get_value(object->sane_opt_id_##id_name(), sane_opts::set_opt_value<type>, &op); \
op.re_order(); \
}
#define SET_SANE_OPT(ret, object, id_name, val) \
{ \
ret = object->set_value(SANE_OPT_ID_##id_name, val); \
}
#define SET_SANE_OPT_EX(ret, object, id_name, val) \
{ \
ret = object->set_value(object->sane_opt_id_##id_name(), val);\
}
#define MAKE_SCANNER_ID(pid, vid) MAKELONG(pid, vid)
#define GET_SCANNER_PID(sid) LOWORD(sid)
#define GET_SCANNER_VID(sid) HIWORD(sid)
extern "C"
{
int initialize_sane(void* reserve);
int open_scanner(SCANNERID scanner_id, ISaneInvoker** invoker, bool last_try);
bool is_scanner_online(SCANNERID scanner_id);
int uninitialize_sane(void* reserve);
void log_debug_info(const char* info, int level);
void log_mem_info(const char* desc, const void* data, size_t bytes);
ISaneInvoker* sane_wapper_open_scanner(SCANNERID scanner_id, int* err, bool last_try);
}

174
twain/sane_helper.cpp Normal file
View File

@ -0,0 +1,174 @@
#include "sane_helper.h"
#include "huagao/brand.h"
#include "utils.h"
#include <string.h>
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// win util
#if OS_WIN
#define LoadLibraryExA win_util::load_dll
namespace win_util
{
std::string get_registry_string(HKEY root, const char* path, const char* name)
{
HKEY key = NULL;
RegOpenKeyA(root, path, &key);
if (!key)
return "";
char* buf = NULL;
DWORD len = 0;
DWORD type = REG_SZ;
std::string ret("");
RegQueryValueExA(key, name, NULL, &type, (LPBYTE)buf, &len);
if (len)
{
buf = new char[len + 4];
memset(buf, 0, (len + 4) * sizeof(*buf));
RegQueryValueExA(key, name, NULL, &type, (LPBYTE)buf, &len);
ret = buf;
delete[] buf;
}
RegCloseKey(key);
return ret;
}
HMODULE load_dll(const char* path_dll, int flag, int* err)
{
HMODULE h = LoadLibraryA(path_dll);
int ret = GetLastError();
utils::to_log(1, "[TWAIN]Load: LoadLibraryA(%s) = %d\r\n", path_dll, ret);
if (!h && (ret == ERROR_MOD_NOT_FOUND || ret == ERROR_BAD_EXE_FORMAT))
{
std::string dir(path_dll);
size_t pos = dir.rfind('\\');
char path[MAX_PATH] = { 0 };
GetDllDirectoryA(_countof(path) - 1, path);
if (pos != std::wstring::npos)
dir.erase(pos);
utils::to_log(LOG_LEVEL_FATAL, "[TWAIN]Load: change directory to '%s' and retry LoadLibraryA(%s) ...\r\n", dir.c_str(), path_dll);
SetDllDirectoryA(dir.c_str());
h = LoadLibraryA(path_dll);
// h = LoadLibraryExW(path_dll, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
ret = GetLastError();
utils::to_log(1, "[TWAIN]Load: trying LoadLibraryA(%s) = %d, restore directory to '%s'\r\n", path_dll, ret, path);
SetDllDirectoryA(path);
}
if (err)
*err = ret;
return h;
}
};
#else
#include <dlfcn.h>
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// sane_helper
sane_helper::sane_helper()
{
// find sane root directory ...
#if OS_WIN
std::string path(win_util::get_registry_string(HKEY_LOCAL_MACHINE, (std::string("Software\\") + PRODUCT_VENDOR + "Scan").c_str(), sizeof(void*) == 4 ? "DriverPath" : "DriverPath64"));
dll_root_ = path + PATH_SEPARATOR;
#else
std::string path(utils::get_module_full_path("libc"));
size_t pos = path.rfind(PATH_SEPARATOR[0]);
if(pos++ != std::string::npos)
{
path.erase(pos);
dll_root_ = std::move(path);
dll_root_ += "sane/";
}
#endif
utils::to_log(1, "sane path is: %s\n", dll_root_.c_str());
}
sane_helper::~sane_helper()
{
clear();
}
void sane_helper::clear(void)
{
invoke_sane_init = nullptr;
invoke_sane_exit = nullptr;
invoke_sane_get_devices = nullptr;
invoke_sane_open = nullptr;
invoke_sane_close = nullptr;
invoke_sane_get_option_descriptor = nullptr;
invoke_sane_control_option = nullptr;
invoke_sane_get_parameters = nullptr;
invoke_sane_start = nullptr;
invoke_sane_read = nullptr;
invoke_sane_cancel = nullptr;
invoke_sane_set_io_mode = nullptr;
invoke_sane_get_select_fd = nullptr;
invoke_sane_strstatus = nullptr;
if(dll_handle_)
{
FreeLibrary(dll_handle_);
dll_handle_ = nullptr;
}
}
bool sane_helper::load_sane(const char* vendor) // libsane_hgsane.so.1, and vendor is 'hgsane'
{
#if OS_WIN
std::string file(dll_root_ + "sane.dll"), func("sane_");
#else
std::string file(dll_root_ + "libsane-" + vendor + ".so.1"), func("sane_");
#endif
bool ok = true;
clear();
dll_handle_ = LoadLibraryExA(file.c_str(), 0, RTLD_NOW);
if(!dll_handle_)
{
utils::to_log(7, "load sane library(%s) = %s\n", file.c_str(), strerror(errno));
return false;
}
func += vendor;
func += "_";
#define GET_PROC_ADDR(api) \
*(void**)&invoke_sane_##api = GetProcAddress(dll_handle_, (func + #api).c_str()); \
ok &= invoke_sane_##api != nullptr;
GET_PROC_ADDR(init);
GET_PROC_ADDR(init_ex);
GET_PROC_ADDR(exit);
GET_PROC_ADDR(get_devices);
GET_PROC_ADDR(open);
GET_PROC_ADDR(close);
GET_PROC_ADDR(get_option_descriptor);
GET_PROC_ADDR(control_option);
GET_PROC_ADDR(get_parameters);
GET_PROC_ADDR(start);
GET_PROC_ADDR(read);
GET_PROC_ADDR(cancel);
GET_PROC_ADDR(set_io_mode);
GET_PROC_ADDR(get_select_fd);
GET_PROC_ADDR(strstatus);
if(!ok)
clear();
return ok;
}

43
twain/sane_helper.h Normal file
View File

@ -0,0 +1,43 @@
// utilities for transfroming options between TWAIN and sane ...
//
// Date: 2022-04-14
//
#pragma once
#include <sane/sane_ex.h>
#include <string>
class sane_helper
{
std::string dll_root_;
HMODULE dll_handle_ = nullptr;
void clear(void);
public:
SANE_Status (*invoke_sane_init) (SANE_Int * version_code, SANE_Auth_Callback authorize) = nullptr;
SANE_Status (*invoke_sane_init_ex)(SANE_Int* version_code, sane_callback cb, void* param) = nullptr;
void (*invoke_sane_exit) (void) = nullptr;
SANE_Status (*invoke_sane_get_devices) (const SANE_Device *** device_list, SANE_Bool local_only) = nullptr;
SANE_Status (*invoke_sane_open) (SANE_String_Const devicename, SANE_Handle * handle) = nullptr;
void (*invoke_sane_close) (SANE_Handle handle) = nullptr;
const SANE_Option_Descriptor* (*invoke_sane_get_option_descriptor) (SANE_Handle handle, SANE_Int option) = nullptr;
SANE_Status (*invoke_sane_control_option) (SANE_Handle handle, SANE_Int option, SANE_Action action, void *value, SANE_Int * info) = nullptr;
SANE_Status (*invoke_sane_get_parameters) (SANE_Handle handle, SANE_Parameters * params) = nullptr;
SANE_Status (*invoke_sane_start) (SANE_Handle handle) = nullptr;
SANE_Status (*invoke_sane_read) (SANE_Handle handle, SANE_Byte * data, SANE_Int max_length, SANE_Int * length) = nullptr;
void (*invoke_sane_cancel) (SANE_Handle handle) = nullptr;
SANE_Status (*invoke_sane_set_io_mode) (SANE_Handle handle, SANE_Bool non_blocking) = nullptr;
SANE_Status (*invoke_sane_get_select_fd) (SANE_Handle handle, SANE_Int * fd) = nullptr;
SANE_String_Const (*invoke_sane_strstatus) (SANE_Status status) = nullptr;
public:
sane_helper();
~sane_helper();
bool load_sane(const char* vendor); // libsane_hgsane.so.1, and vendor is 'hgsane'
};
extern sane_helper* sane_helper_;

348
twain/sane_option_trans.cpp Normal file
View File

@ -0,0 +1,348 @@
#include "sane_option_trans.h"
#include <sane/sane_option_definitions.h>
#include <string.h>
#include "scanned_img.h"
enum Language // copy from <twain/twpp/enums.hpp>
{
English = 2,
ChineseSimplified = 41,
ChineseTraditional = 43,
};
namespace sane_opt_trans
{
static struct
{
int twain_id;
const char* opt_val;
}
g_color_mode_map[] = {{COLOR_BW, OPTION_VALUE_YSMS_HB}
, {COLOR_GRAY, OPTION_VALUE_YSMS_256JHD}
, {COLOR_RGB, OPTION_VALUE_YSMS_24WCS}
, {COLOR_AUTO_MATCH, OPTION_VALUE_YSMS_YSZDSB}
},
g_multiout_map[] = { {MULTI_OUT_NONE, "\346\227\240"}
, {MULTI_OUT_ALL, OPTION_VALUE_DLSCLX_CS_HD_HB}
, {MULTI_OUT_COLOR_GRAY, OPTION_VALUE_DLSCLX_CS_HD}
, {MULTI_OUT_COLOR_BW, OPTION_VALUE_DLSCLX_CS_HB}
, {MULTI_OUT_GRAY_BW, OPTION_VALUE_DLSCLX_HD_HB}
},
g_enhance_map[] = { {ENHANCE_NONE, OPTION_VALUE_HDHHBTX_CSYZQ_BCS}
, {ENHANCE_RED, OPTION_VALUE_HDHHBTX_CSYZQ_HSZQ}
, {ENHANCE_GREEN, OPTION_VALUE_HDHHBTX_CSYZQ_LSZQ}
, {ENHANCE_BLUE, OPTION_VALUE_HDHHBTX_CSYZQ_LANSEZENGQIANG}
},
g_filter_map[] = { {FILTER_NONE, OPTION_VALUE_HDHHBTX_CSYZQ_BCS}
, {FILTER_RED, OPTION_VALUE_HDHHBTX_CSYZQ_CHS}
, {FILTER_GREEN, OPTION_VALUE_HDHHBTX_CSYZQ_CLS}
, {FILTER_BLUE, OPTION_VALUE_HDHHBTX_CSYZQ_CHULANSE}
},
g_auto_color_map[] = { {0, OPTION_VALUE_YSMS_HB}
, {1, OPTION_VALUE_YSMS_256JHD}
},
g_sharpen_map[] = { {SHARPEN_NONE, OPTION_VALUE_RHYMH_W}
, {SHARPEN_SHARPEN, OPTION_VALUE_RHYMH_RH}
, {SHARPEN_SHARPEN_MORE, OPTION_VALUE_RHYMH_JYBRH}
, {SHARPEN_BLUR, OPTION_VALUE_RHYMH_MH}
, {SHARPEN_BLUR_MORE, OPTION_VALUE_RHYMH_JYBMH}
},
g_paper_map[] = { {PAPER_LETTER, OPTION_VALUE_ZZCC_Letter}
, {PAPER_LEGAL, OPTION_VALUE_ZZCC_LEGAL}
//, {PAPER_8K, OPTION_VALUE_ZZCC_8K}
//, {PAPER_16K, OPTION_VALUE_ZZCC_16K}
, {PAPER_A3, OPTION_VALUE_ZZCC_A3}
, {PAPER_A4, OPTION_VALUE_ZZCC_A4}
, {PAPER_A5, OPTION_VALUE_ZZCC_A5}
, {PAPER_A6, OPTION_VALUE_ZZCC_A6}
, {PAPER_B4, OPTION_VALUE_ZZCC_B4}
, {PAPER_B5, OPTION_VALUE_ZZCC_B5}
, {PAPER_B6, OPTION_VALUE_ZZCC_B6}
, {PAPER_STATEMENT, OPTION_VALUE_ZZCC_PPYSCC}
, {PAPER_MAXSIZE, OPTION_VALUE_ZZCC_ZDSMCC}
, {PAPER_MAXSIZE_CROP, OPTION_VALUE_ZZCC_ZDSMCCZDCQ}
, {PAPER_DOUBLE_LETTER, OPTION_VALUE_ZZCC_DoubleLetter}
, {PAPER_TRIPPLE, OPTION_VALUE_ZZCC_SLSJ}
},
g_language[] = { {Language::ChineseSimplified, "\344\270\255\346\226\207\357\274\210\347\256\200\344\275\223\357\274\211"}
, {Language::ChineseTraditional, "\344\270\255\346\226\207\357\274\210\347\271\201\344\275\223\357\274\211"}
, {Language::English, "\350\213\261\350\257\255"}
},
g_sleep[] = { {SANE_POWER_NONE, OPTION_VALUE_XMSJ_BXM}
, {SANE_POWER_MINUTES_5, OPTION_VALUE_XMSJ_WFZ}
, {SANE_POWER_MINUTES_10, OPTION_VALUE_XMSJ_SFZ}
, {SANE_POWER_MINUTES_30, OPTION_VALUE_XMSJ_BXS}
, {SANE_POWER_MINUTES_60, OPTION_VALUE_XMSJ_YXS}
, {SANE_POWER_MINUTES_120, OPTION_VALUE_XMSJ_LXS}
, {SANE_POWER_MINUTES_240, OPTION_VALUE_XMSJ_SXS}
}
;
#define VALUE_FROM_TWAIN(arr, val) \
for (int i = 0; i < _countof(arr); ++i) \
{ \
if (arr[i].twain_id == val) \
return local_trans::from_default_language(arr[i].opt_val, NULL); \
}
#define VALUE_TO_TWAIN(arr, val) \
for (int i = 0; i < _countof(arr); ++i) \
{ \
if (strcmp(local_trans::from_default_language(arr[i].opt_val, NULL), val) == 0) \
return arr[i].twain_id; \
}
const char* color_mode_from_twain(int val)
{
VALUE_FROM_TWAIN(g_color_mode_map, val);
return OPTION_VALUE_YSMS_YSZDSB;
}
int color_mode_to_twain(const char* val)
{
VALUE_TO_TWAIN(g_color_mode_map, val);
return COLOR_AUTO_MATCH;
}
const char* multiout_value_from_twain(int val)
{
VALUE_FROM_TWAIN(g_multiout_map, val);
return NULL;
}
int multiout_value_to_twain(const char* val)
{
VALUE_TO_TWAIN(g_multiout_map, val);
return -1;
}
const char* filter_enhance_value_from_twain(int val, bool filter)
{
if (filter)
{
for (int i = 0; i < _countof(g_filter_map); ++i)
{
if (g_filter_map[i].twain_id == val)
return local_trans::from_default_language(g_filter_map[i].opt_val, NULL);
}
}
else
{
for (int i = 0; i < _countof(g_enhance_map); ++i)
{
if (g_enhance_map[i].twain_id == val)
return local_trans::from_default_language(g_enhance_map[i].opt_val, NULL);
}
}
return local_trans::from_default_language(OPTION_VALUE_HDHHBTX_CSYZQ_BCS, NULL);
}
int filter_enhance_value_to_twain(const char* val, bool* is_filter)
{
bool type = false;
const char* hz = local_trans::to_default_language(val, NULL);
if (!is_filter)
is_filter = &type;
*is_filter = true;
for (int i = 0; i < _countof(g_filter_map); ++i)
{
if (strcmp(g_filter_map[i].opt_val, hz) == 0)
return g_filter_map[i].twain_id;
}
*is_filter = false;
for (int i = 0; i < _countof(g_enhance_map); ++i)
{
if (strcmp(g_enhance_map[i].opt_val, hz) == 0)
return g_enhance_map[i].twain_id;
}
return ENHANCE_NONE;
}
const char* text_direction_from_twain(float val)
{
while (val < .0f)
val += 360.0f;
while (val > 360.0f)
val -= 360.0f;
if (60.0f < val && val < 120.0f)
return local_trans::from_default_language(OPTION_VALUE_WGFX_90, NULL);
else if (150.0f < val && val < 210.0f)
return local_trans::from_default_language(OPTION_VALUE_WGFX_180, NULL);
else if (240.0f < val && val < 300.0f)
return local_trans::from_default_language(OPTION_VALUE_WGFX__90, NULL);
else if (330.0f < val || val < 30.0f)
return local_trans::from_default_language(OPTION_VALUE_WGFX_0, NULL);
else
return local_trans::from_default_language(OPTION_VALUE_WGFX_ZDWBFXSB, NULL);
}
float text_direction_to_twain(const char* val)
{
const char* hz = local_trans::to_default_language(val, NULL);
if (strcmp(hz, OPTION_VALUE_WGFX_90) == 0)
return 90.0f;
else if (strcmp(hz, OPTION_VALUE_WGFX_180) == 0)
return 180.0f;
else if (strcmp(hz, OPTION_VALUE_WGFX__90) == 0)
return 270.0f;
else if (strcmp(hz, OPTION_VALUE_WGFX_0) == 0)
return .0f;
else
return AUTO_MATIC_ROTATE;
}
struct
{
const char* normal;
const char* lateral;
}g_lateral_map[] =
{ {OPTION_VALUE_ZZCC_A4, OPTION_VALUE_ZZCC_A4HX}
, {OPTION_VALUE_ZZCC_16K, OPTION_VALUE_ZZCC_16KHX}
, {OPTION_VALUE_ZZCC_A5, OPTION_VALUE_ZZCC_A5HX}
, {OPTION_VALUE_ZZCC_A6, OPTION_VALUE_ZZCC_A6HX}
, {OPTION_VALUE_ZZCC_B5, OPTION_VALUE_ZZCC_B5HX}
, {OPTION_VALUE_ZZCC_B6, OPTION_VALUE_ZZCC_B6HX}
, {OPTION_VALUE_ZZCC_Letter, OPTION_VALUE_ZZCC_LetterHX}
};
const char* paper_from_twain(int val)
{
if (val == 0) // modified for TWAIN, consider this value as PAPER_STATEMENT. 2023-06-03
val = PAPER_STATEMENT;
VALUE_FROM_TWAIN(g_paper_map, val);
return NULL;
}
int paper_to_twain(const char* val)
{
VALUE_TO_TWAIN(g_paper_map, val);
return -1;
}
const char* switch_paper_lateral(const char* val)
{
const char* hz = local_trans::to_default_language(val, NULL);
for (int i = 0; i < _countof(g_lateral_map); ++i)
{
if (strcmp(g_lateral_map[i].normal, hz) == 0)
return local_trans::from_default_language(g_lateral_map[i].lateral, NULL);
else if (strcmp(g_lateral_map[i].lateral, hz) == 0)
return local_trans::from_default_language(g_lateral_map[i].normal, NULL);
}
return NULL;
}
bool is_paper_lateral(const char* val)
{
const char* hz = local_trans::to_default_language(val, NULL);
for (int i = 0; i < _countof(g_lateral_map); ++i)
{
if (strcmp(g_lateral_map[i].lateral, hz) == 0)
return true;
}
return false;
}
const char* auto_color_type_from_twain(int val)
{
VALUE_FROM_TWAIN(g_color_mode_map, val);
return local_trans::from_default_language(OPTION_VALUE_YSMS_24WCS, NULL);
}
int auto_color_type_to_twain(const char* val)
{
VALUE_TO_TWAIN(g_color_mode_map, val);
return -1;
}
const char* sharpen_from_twain(int val)
{
VALUE_FROM_TWAIN(g_sharpen_map, val);
return local_trans::from_default_language(OPTION_VALUE_RHYMH_W, NULL);
}
int sharpen_to_twain(const char* val)
{
VALUE_TO_TWAIN(g_sharpen_map, val);
return SHARPEN_NONE;
}
struct {
int twain;
int sane;
}
g_compression_map[] = { {0, SANE_COMPRESSION_NONE}
, {5, SANE_COMPRESSION_GROUP4}
}
;
#define INT_FROM_TWAIN(arr, val) \
for (int i = 0; i < _countof(arr); ++i) \
{ \
if (arr[i].twain == val) \
return arr[i].sane; \
}
#define INT_TO_TWAIN(arr, val) \
for (int i = 0; i < _countof(arr); ++i) \
{ \
if (arr[i].sane == val) \
return arr[i].twain; \
}
int compression_from_twain(int val)
{
INT_FROM_TWAIN(g_compression_map, val);
return SANE_COMPRESSION_NONE;
}
int compression_to_twain(int val)
{
INT_TO_TWAIN(g_compression_map, val);
return 0;
}
std::vector<int> support_image_types(void)
{
std::vector<int> it;
it.push_back(SANE_IMAGE_TYPE_BMP);
it.push_back(SANE_IMAGE_TYPE_TIFF);
it.push_back(SANE_IMAGE_TYPE_JFIF);
return it;
}
int language_to_twain(const char* language)
{
VALUE_TO_TWAIN(g_language, language);
return Language::ChineseSimplified;
}
const char* language_from_twain(int language)
{
VALUE_FROM_TWAIN(g_language, language);
VALUE_FROM_TWAIN(g_language, Language::ChineseSimplified);
}
int sleep_time(const char* str)
{
VALUE_TO_TWAIN(g_sleep, str);
VALUE_TO_TWAIN(g_sleep, OPTION_VALUE_XMSJ_BXM);
}
const char* sleep_time(int val)
{
VALUE_FROM_TWAIN(g_sleep, val);
VALUE_FROM_TWAIN(g_sleep, SANE_POWER_NONE);
}
}

47
twain/sane_option_trans.h Normal file
View File

@ -0,0 +1,47 @@
// utilities for transfroming options between TWAIN and sane ...
//
// Date: 2022-04-14
//
#pragma once
#include "s2t_api.h"
namespace sane_opt_trans
{
const char* color_mode_from_twain(int val);
int color_mode_to_twain(const char* val);
const char* multiout_value_from_twain(int val);
int multiout_value_to_twain(const char* val);
const char* filter_enhance_value_from_twain(int val, bool filter);
int filter_enhance_value_to_twain(const char* val, bool* is_filter);
const char* text_direction_from_twain(float val);
float text_direction_to_twain(const char* val);
const char* paper_from_twain(int val);
int paper_to_twain(const char* val);
const char* switch_paper_lateral(const char* val);
bool is_paper_lateral(const char* val);
const char* auto_color_type_from_twain(int val);
int auto_color_type_to_twain(const char* val);
const char* sharpen_from_twain(int val);
int sharpen_to_twain(const char* val);
int compression_from_twain(int val);
int compression_to_twain(int val);
std::vector<int> support_image_types(void);
int language_to_twain(const char* language);
const char* language_from_twain(int language);
int sleep_time(const char* str);
const char* sleep_time(int val);
}

627
twain/scanned_img.cpp Normal file
View File

@ -0,0 +1,627 @@
#include "scanned_img.h"
#include "sane_helper.h"
#include <string.h>
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
extern sane_helper* sane_helper_;
namespace local_trans
{
static const char* (*invoke_to_default_language)(const char* str, bool* ok) = nullptr;
static const char* (*invoke_from_default_language)(const char* str, bool* ok) = nullptr;
int load_lang_pak(const char* dll)
{
HMODULE h = LoadLibraryExA(dll, 0, RTLD_NOW);
if(!h)
return GetLastError();
FARPROC *pfn = (FARPROC*)&invoke_from_default_language;
*pfn = GetProcAddress(h, "from_default_language");
pfn = (FARPROC*)&invoke_to_default_language;
*pfn = GetProcAddress(h, "to_default_language");
return invoke_to_default_language && invoke_to_default_language ? 0 : ENOENT;
}
std::string lang_trans_between_hz936(const char* in, bool from_hz)
{
std::string a(lang_trans_between_hz936(in, from_hz, nullptr));
return std::move(a);
}
const char* lang_trans_between_hz936(const char* in, bool from_hz, void* param)
{
if(from_hz && invoke_from_default_language)
return invoke_from_default_language(in, nullptr);
else if(!from_hz && invoke_to_default_language)
return invoke_to_default_language(in, nullptr);
else
return in;
}
const char* to_default_language(const char* str, bool* ok)
{
if(invoke_to_default_language)
return invoke_to_default_language(str, ok);
if(ok)
*ok = false;
return str;
}
const char* from_default_language(const char* str, bool* ok)
{
if(invoke_from_default_language)
return invoke_from_default_language(str, ok);
if(ok)
*ok = false;
return str;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// class refer
refer::refer() : ref_(1)
{}
refer::~refer()
{}
COM_API_IMPLEMENT(refer, long, add_ref(void))
{
std::lock_guard<std::mutex> locker(lock_);
return ++ref_;
}
COM_API_IMPLEMENT(refer, long, release(void))
{
long ref = 0;
{
std::lock_guard<std::mutex> locker(lock_);
ref = --ref_;
}
if (ref == 0)
delete this;
return ref;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// class image_buf
const unsigned int max_mem_block = 2 * 1024 * 1024;
extern void log_info(const char* info, int level);
image_buf::image_buf() : bytes_(0), offset_(0), mapped_bytes_(0), is_mem_(true), buf_(nullptr)
{}
image_buf::~image_buf()
{
close();
}
void image_buf::close(void)
{
if(buf_)
{
free(buf_);
buf_ = nullptr;
}
bytes_ = offset_ = mapped_bytes_ = 0;
is_mem_ = true;
}
unsigned char* image_buf::allocate(const char* file, unsigned long long size, bool force_file)
{
close();
if(force_file)
return nullptr;
buf_ = (unsigned char*)malloc(size);
if(buf_)
{
memset(buf_, 0, size);
bytes_ = mapped_bytes_ = size;
}
return buf_;
}
bool image_buf::save(const void* data, size_t* bytes, unsigned long long off)
{
if(off + *bytes <= mapped_bytes_)
{
memcpy(buf_ + off, data, *bytes);
//if(bytes_ < off + *bytes)
// bytes_ = off + *bytes;
return true;
}
else
{
if(off < mapped_bytes_)
{
memcpy(buf_ + off, data, mapped_bytes_ - off);
*bytes = mapped_bytes_ - off;
}
else
*bytes = 0;
//if(bytes_ < off + *bytes)
// bytes_ = off + *bytes;
return false;
}
}
bool image_buf::save(unsigned long long off, image_buf* src)
{
if(off + src->bytes() > mapped_bytes_)
return false;
memcpy(buf_ + off, src->buf_, src->bytes());
//if(bytes_ < off + src->bytes())
// bytes_ = off + src->bytes();
return true;
}
unsigned long long image_buf::bytes(void)
{
return bytes_;
}
unsigned long long image_buf::size(void)
{
return mapped_bytes_;
}
unsigned char* image_buf::buffer(unsigned long long off, unsigned int* bytes)
{
if(off < mapped_bytes_)
{
if(*bytes > mapped_bytes_ - off)
*bytes = mapped_bytes_ - off;
return buf_ + off;
}
return nullptr;
}
int image_buf::read(void* buf, size_t* bytes, unsigned long long off)
{
if (!bytes)
return SCANNER_ERR_INVALID_PARAMETER;
if(off >= bytes_)
return SCANNER_ERR_OUT_OF_RANGE;
if(*bytes > bytes_ - off)
{
*bytes = bytes_ - off;
}
memcpy(buf, buf_ + off, *bytes);
return SCANNER_ERR_OK;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// class scanned_img
scanned_img::scanned_img(SANE_Handle dev, SANE_Parameters head, int dpi
, const char* tmp_file, twain_xfer xfer
, SANE_FinalImgFormat *fmt) : head_(head), dpi_(dpi), header_size_(0), file_(tmp_file ? tmp_file : "")
, dev_(dev), status_(SANE_Image_Statu_OK)
{
if (fmt)
fmt_ = *fmt;
else
{
fmt_.img_format = SANE_IMAGE_TYPE_BMP;
fmt_.detail = 0;
}
size_t bytes = line_bytes() * height();
std::string h(file_header(fmt_.img_format, (float)dpi, xfer));
unsigned char* dst = NULL;
bool ok = false;
data_ = new image_buf();
header_size_ = h.length();
dst = data_->allocate(tmp_file, bytes + h.length());
if (dst)
{
unsigned long long off = 0, total = 0;
bytes = h.length();
if (data_->save(h.c_str(), &bytes, off))
{
unsigned int len = line_bytes();
unsigned long long line = line_bytes();
if (xfer == TWAIN_XFER_Memory)
line *= -1;
else
off = data_->bytes() - line;
dst = data_->buffer(off, &len);
int want_to_read = head_.bytes_per_line, rcv = 0, dif = line_bytes() - head_.bytes_per_line;
while (dst)
{
int r = want_to_read > (int)(len) - rcv ? (int)(len)- rcv : want_to_read;
int ret = sane_helper_->invoke_sane_read(dev, dst + rcv, r, &r);
total += r;
if (ret != SANE_STATUS_GOOD)
break;
want_to_read -= r;
rcv += r;
if (want_to_read == 0)
{
want_to_read = head_.bytes_per_line;
off -= line;
len = line_bytes();
rcv = 0;
dst = data_->buffer(off, &len);
total += dif;
}
else
{
len = want_to_read;
dst = data_->buffer(off + rcv, &len);
}
}
ok = total + h.length() + dif == data_->bytes();
}
}
do_result(ok, xfer);
}
scanned_img::scanned_img(SANE_Handle dev, SANE_Parameters head, void* data, unsigned int len, int dpi, const char* tmp_file
, twain_xfer xfer, SANE_FinalImgFormat* fmt) : head_(head), dpi_(dpi), header_size_(0)
, file_(tmp_file ? tmp_file : ""), dev_(dev), status_(SANE_Image_Statu_OK)
{
if (fmt)
fmt_ = *fmt;
else
{
fmt_.img_format = SANE_IMAGE_TYPE_BMP;
fmt_.detail = 0;
}
size_t bytes = line_bytes() * head.lines;
std::string h(file_header(fmt_.img_format, (float)dpi, xfer));
unsigned char* dst = NULL, *src = (unsigned char*)data;
bool ok = false;
header_size_ = h.length();
data_ = new image_buf();
bytes += header_size_;
dst = data_->allocate(tmp_file, bytes);
bytes = h.length();
if (data_->save(h.c_str(), &bytes, 0))
{
unsigned long long off = bytes, line_l = line_bytes();
unsigned int buf_len = line_bytes(), row = 0;
if (xfer == TWAIN_XFER_Memory)
line_l *= -1;
else
off = data_->size() - line_l;
for (; row < (unsigned int)head.lines; ++row)
{
bytes = head.bytes_per_line;
if (!data_->save(src, &bytes, off))
break;
off -= line_l;
src += head.bytes_per_line;
}
ok = row == head.lines;
}
do_result(ok, xfer);
}
scanned_img::~scanned_img()
{
if (data_)
delete data_;
}
void scanned_img::set_image_status(SANE_Image_Statu status)
{
status_ = status;
}
std::string scanned_img::file_header(SANE_ImageType type, float resolution, twain_xfer xfer)
{
std::string h("");
if (type == SANE_IMAGE_TYPE_BMP /*&& xfer != TWAIN_XFER_Memory*/)
{
BITMAPINFOHEADER bih = { 0 };
int pal_size = 0;
bih.biSize = sizeof(bih);
bih.biWidth = width();
bih.biBitCount = depth();
bih.biSizeImage = line_bytes() * height();
bih.biPlanes = 1;
bih.biHeight = height();
bih.biCompression = BI_RGB;
bih.biXPelsPerMeter = bih.biYPelsPerMeter = (LONG)(resolution * 39.37f + .5f);
if (bih.biBitCount == 1)
pal_size = 2 * sizeof(int);
else if (bih.biBitCount == 8)
pal_size = 256 * sizeof(int);
/*if (xfer == TWAIN_XFER_File)*/
{
BITMAPFILEHEADER fh = { 0 };
fh.bfType = MAKEWORD('B', 'M');
fh.bfSize = sizeof(fh) + bih.biSizeImage + sizeof(bih) + pal_size;
fh.bfOffBits = sizeof(fh) + sizeof(bih) + pal_size;
h = std::string((char*)&fh, sizeof(fh));
}
h += std::string((char*)&bih, sizeof(bih));
pal_size_ = pal_size;
if (bih.biBitCount == 1)
{
int pal[] = { 0, 0x0ffffff };
h += std::string((char*)pal, pal_size);
}
else if (bih.biBitCount == 8)
{
static unsigned int g_bmp8_pallete[256] = { 0 };
if (g_bmp8_pallete[1] == 0)
{
for (int i = 1; i < _countof(g_bmp8_pallete); ++i)
g_bmp8_pallete[i] = MAKELONG(MAKEWORD(i, i), MAKEWORD(i, 0));
}
h += std::string((char*)g_bmp8_pallete, pal_size);
}
}
return h;
}
void scanned_img::do_result(bool ok, twain_xfer xfer)
{
if (ok)
{
if (fmt_.img_format == SANE_IMAGE_TYPE_BMP &&
fmt_.compress.compression == SANE_COMPRESSION_GROUP4 &&
xfer == TWAIN_XFER_Memory)
{
// convert to black-white ...
std::string head(file_header(SANE_IMAGE_TYPE_BMP, (float)dpi_, TWAIN_XFER_File));
size_t size = head.length();
image_buf* buf = new image_buf();
std::string file(file_ + ".tmp");
unsigned long long off = 0;
if (buf->allocate(file.c_str(), size + data_->bytes(), false) &&
buf->save(head.c_str(), &size, off))
{
if (buf->save(size, data_))
{
SANE_ImageFormatConvert conv;
std::string sf(file);
SANE_Int after = 0;
conv.src.data_len = buf->bytes();
conv.src.data = (SANE_String_Const)buf->buffer(0, &conv.src.data_len);
conv.src.fmt.img_format = SANE_IMAGE_TYPE_BMP;
conv.src.fmt.compress.compression = SANE_COMPRESSION_NONE;
conv.src.is_file = SANE_FALSE;
conv.dst.data = NULL;
conv.dst.data_len = 0;
conv.dst.fmt.img_format = SANE_IMAGE_TYPE_BMP;
conv.dst.fmt.compress.compression = SANE_COMPRESSION_GROUP4;
conv.dst.fmt.compress.detail = NULL;
conv.dst.is_file = false;
if (sane_helper_->invoke_sane_control_option(dev_, SANE_OPT_ID_TRANSFORM_IMAGE_FORMAT, SANE_ACTION_SET_VALUE, &conv, &after) == SANE_STATUS_GOOD)
{
delete data_;
data_ = new image_buf();
size = conv.dst.data_len;
data_->allocate(file_.c_str(), conv.dst.data_len);
data_->save(conv.dst.data, &size, 0);
sane_helper_->invoke_sane_control_option(dev_, SANE_OPT_ID_FREE_BUFFER, SANE_ACTION_SET_VALUE, (void*)&conv.dst.data, &after);
head_.format = SANE_FRAME_GRAY;
head_.depth = 1;
head_.bytes_per_line = (head_.pixels_per_line + 7) / 8;
}
}
}
if (buf)
delete buf;
}
else if (fmt_.img_format == SANE_IMAGE_TYPE_BMP
&& channel() == 3
&& xfer != TWAIN_XFER_Memory)
{
// swap RGB
swap_rgb();
}
}
else
{
delete data_;
data_ = NULL;
header_size_ = 0;
}
}
void scanned_img::swap_rgb(void)
{
unsigned long long off = 0;
unsigned int line = line_bytes(), len = line;
unsigned char* dst = NULL;
for (int i = 0; i < height(); ++i)
{
int l = head_.bytes_per_line, cur = 0;
off = i * line + header_size_;
while (l > 0)
{
len = l;
dst = data_->buffer(off + cur, &len);
if (!dst)
break;
if (len > (unsigned int)l)
len = l;
len /= 3;
for (int pos = 0; pos < (int)len; ++pos)
{
unsigned char uc = dst[pos * 3 + 0];
dst[pos * 3 + 0] = dst[pos * 3 + 2];
dst[pos * 3 + 2] = uc;
}
l -= len * 3;
cur += len * 3;
}
if (!dst)
break;
}
}
// IRef
COM_API_IMPLEMENT(scanned_img, long, add_ref(void))
{
return refer::add_ref();
}
COM_API_IMPLEMENT(scanned_img, long, release(void))
{
return refer::release();
}
// IScanImg
COM_API_IMPLEMENT(scanned_img, int, width(void))
{
return head_.pixels_per_line;
}
COM_API_IMPLEMENT(scanned_img, int, line_bytes(void))
{
if (fmt_.img_format == SANE_IMAGE_TYPE_BMP && head_.depth >= 8)
return (head_.bytes_per_line + 3) / 4 * 4;
else
return head_.bytes_per_line;
}
COM_API_IMPLEMENT(scanned_img, int, height(void))
{
return head_.lines;
}
COM_API_IMPLEMENT(scanned_img, int, depth(void))
{
if (head_.format == SANE_FRAME_RGB)
return head_.depth * 3;
else
return head_.depth;
}
COM_API_IMPLEMENT(scanned_img, int, channel(void))
{
return head_.format == SANE_FRAME_RGB ? 3 : 1;
}
COM_API_IMPLEMENT(scanned_img, int, dpi(void))
{
return dpi_;
}
COM_API_IMPLEMENT(scanned_img, SANE_Frame, type(void))
{
return head_.format;
}
COM_API_IMPLEMENT(scanned_img, unsigned int, bytes(void))
{
return data_ ? (unsigned int)data_->bytes() : 0;
}
COM_API_IMPLEMENT(scanned_img, unsigned int, header_size(void))
{
return header_size_;
}
COM_API_IMPLEMENT(scanned_img, unsigned char*, data(unsigned long long off, unsigned int* bytes))
{
return data_ ? data_->buffer(off, bytes) : NULL;
}
COM_API_IMPLEMENT(scanned_img, int, read(void* buf, size_t* bytes, unsigned long long off))
{
return data_ ? data_->read(buf, bytes, off) : SCANNER_ERR_NO_DATA;
}
COM_API_IMPLEMENT(scanned_img, const char*, file(void))
{
return "";
}
COM_API_IMPLEMENT(scanned_img, void, keep_file(bool keep))
{
}
COM_API_IMPLEMENT(scanned_img, void, copy_header(SANE_Parameters* head))
{
*head = head_;
}
COM_API_IMPLEMENT(scanned_img, int, image_status(void))
{
return status_;
}
COM_API_IMPLEMENT(scanned_img, size_t, get_bits_offset(void))
{
if (fmt_.img_format == SANE_IMAGE_TYPE_BMP)
return sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + pal_size_;
else
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// class safe_img_queue
safe_img_queue::safe_img_queue()
{}
safe_img_queue::~safe_img_queue()
{
clear();
}
void safe_img_queue::access_image(scanned_img* img)
{
img->add_ref();
}
void safe_img_queue::free_image(scanned_img* img)
{
img->release();
}
bool safe_img_queue::get_header(SANE_Parameters* header, size_t* bytes, int* dpi)
{
scanned_img *img = take(false, &safe_img_queue::access_image);
bool ok = false;
if (bytes)
*bytes = 0;
if (img)
{
if(header)
img->copy_header(header);
if(bytes)
*bytes = img->bytes();
if (dpi)
*dpi = img->dpi();
ok = true;
img->release();
}
return ok;
}
void safe_img_queue::clear()
{
safe_queue<scanned_img*>::clear(&safe_img_queue::free_image);
}

194
twain/scanned_img.h Normal file
View File

@ -0,0 +1,194 @@
#pragma once
#include "s2t_api.h"
#include <vector>
#include <mutex>
class refer : public IRef
{
std::mutex lock_;
volatile long ref_;
protected:
refer();
virtual ~refer();
// IRef
public:
COM_API_OVERRIDE(long, add_ref(void));
COM_API_OVERRIDE(long, release(void));
};
class image_buf
{
unsigned long long bytes_;
unsigned long long offset_;
unsigned int mapped_bytes_;
bool is_mem_;
unsigned char* buf_;
void close(void);
public:
image_buf();
~image_buf();
public:
unsigned char* allocate(const char* file, unsigned long long size = 0, bool force_file = false);
bool save(const void* data, size_t* bytes, unsigned long long off);
bool save(unsigned long long off, image_buf* src);
unsigned long long bytes(void);
unsigned long long size(void);
unsigned char* buffer(unsigned long long off, unsigned int* bytes);
int read(void* buf, size_t* bytes, unsigned long long off);
};
class scanned_img : public IScanImg, virtual public refer
{
SANE_Parameters head_;
int dpi_;
SANE_Handle dev_;
std::string file_;
unsigned int header_size_;
SANE_FinalImgFormat fmt_;
SANE_Image_Statu status_;
image_buf* data_;
size_t pal_size_ = 0;
std::string file_header(SANE_ImageType type, float resolution, twain_xfer xfer);
void do_result(bool ok, twain_xfer xfer);
void swap_rgb(void);
public:
scanned_img(SANE_Handle dev, SANE_Parameters head, int dpi, const char* tmp_file
, twain_xfer xfer = TWAIN_XFER_Native, SANE_FinalImgFormat *fmt = NULL);
scanned_img(SANE_Handle dev, SANE_Parameters head, void* data, unsigned int len, int dpi, const char* tmp_file
, twain_xfer xfer = TWAIN_XFER_Native, SANE_FinalImgFormat *fmt = NULL);
void set_image_status(SANE_Image_Statu status);
protected:
~scanned_img();
// IRef
public:
COM_API_OVERRIDE(long, add_ref(void));
COM_API_OVERRIDE(long, release(void));
// IScanImg
public:
COM_API_OVERRIDE(int, width(void));
COM_API_OVERRIDE(int, line_bytes(void));
COM_API_OVERRIDE(int, height(void));
COM_API_OVERRIDE(int, depth(void));
COM_API_OVERRIDE(int, channel(void));
COM_API_OVERRIDE(int, dpi(void));
COM_API_OVERRIDE(SANE_Frame, type(void));
COM_API_OVERRIDE(unsigned int, bytes(void));
COM_API_OVERRIDE(unsigned int, header_size(void));
COM_API_OVERRIDE(unsigned char*, data(unsigned long long off, unsigned int* bytes));
COM_API_OVERRIDE(int, read(void* buf, size_t* bytes, unsigned long long off = 0));
COM_API_OVERRIDE(const char*, file(void));
COM_API_OVERRIDE(void, keep_file(bool keep));
COM_API_OVERRIDE(void, copy_header(SANE_Parameters* head));
COM_API_OVERRIDE(int, image_status(void));
COM_API_OVERRIDE(size_t, get_bits_offset(void));
};
template<class T>
class safe_queue
{
typedef struct _t_and_size
{
size_t bytes;
T t;
}TNS;
std::mutex lock_;
std::vector<TNS> queue_;
size_t bytes_;
T empty_;
public:
safe_queue() : bytes_(0)
{}
virtual ~safe_queue()
{}
public:
size_t count(size_t* bytes = NULL)
{
std::lock_guard<std::mutex> lock(lock_);
if (bytes)
*bytes = bytes_;
return queue_.size();
}
bool save(T v, size_t bytes)
{
std::lock_guard<std::mutex> lock(lock_);
TNS tns = { bytes, v };
queue_.push_back(tns);
bytes_ += bytes;
return true;
}
T take(bool remove = true, void(* first)(T) = NULL)
{
std::lock_guard<std::mutex> lock(lock_);
TNS t;
if (queue_.size())
{
t = queue_[0];
if (remove)
{
queue_.erase(queue_.begin());
bytes_ -= t.bytes;
}
if (first)
first(t.t);
}
else
t.t = empty_;
return t.t;
}
void clear(void(* tfree)(T) = NULL)
{
std::lock_guard<std::mutex> lock(lock_);
if (tfree)
{
for (auto& v : queue_)
tfree(v.t);
}
queue_.clear();
bytes_ = 0;
}
};
class safe_img_queue : public safe_queue<scanned_img*>
{
static void access_image(scanned_img* img);
static void free_image(scanned_img* img);
public:
safe_img_queue();
~safe_img_queue();
public:
bool get_header(SANE_Parameters* header, size_t* bytes = NULL, int *dpi = NULL);
void clear();
};
namespace local_trans
{
int load_lang_pak(const char* dll);
std::string lang_trans_between_hz936(const char* in, bool from_hz = true);
const char* lang_trans_between_hz936(const char* in, bool from_hz, void* param);
const char* to_default_language(const char* str, bool* ok);
const char* from_default_language(const char* str, bool* ok = NULL);
}

3285
twain/scanner.cpp Normal file

File diff suppressed because it is too large Load Diff

349
twain/scanner.h Normal file
View File

@ -0,0 +1,349 @@
#pragma once
#include "scanned_img.h"
#include <vector>
#include <algorithm>
#include <memory>
#include <functional>
#include <map>
#include <thread>
#define SANE_OPTION_ID(name) \
SANE_OPTION_ID_OVERRIDE(name); \
int name##_id_ = -1;
#define SANE_OPTION_ID_IMPLEMENT(name) \
SANE_OPTION_IMPLEMENT(scanner, name) \
{ \
return name##_id_; \
}
#define EX_HANDLER_PROTO(name) \
int name(int base_id, void* data, set_opt_value setv)
#define EX_OPTION_HANDLER_DECL(name) EX_HANDLER_PROTO(handle_ex_##name)
#define EX_OPTION_HANDLER_IMPL(name) EX_HANDLER_PROTO(scanner::handle_ex_##name)
#define EXTENSION_ID_BASE 0x300
class dlg_indicator;
class dlg_setting;
namespace gb
{
class scanner_cfg;
}
class scanner : public ISaneInvoker, virtual public refer
{
typedef struct _simple_opt
{
value_type type;
value_limit limit;
int bytes;
}SIMPLEOPT;
SANE_Handle handle_;
SCANNERID id_;
int err_;
int ex_id_;
int prev_start_result_;
int dpi_;
int fetch_imgs_ = 0; // count for images has fetched by APP
bool is_bIndicator;
bool is_show_setting_;
unsigned int img_ind_;
std::string scanner_name_;
std::string tmp_path_;
std::string cfg_path_;
std::string scan_msg_;
bool scan_err_;
volatile bool is_ui_wait_img_;
volatile bool is_scanning_;
volatile bool user_cancel_;
twain_xfer xfer_;
safe_img_queue images_;
size_t max_img_mem_;
safe_queue<int> events_; //如果有界面,则全部保存从界面传回的消息;否则只保存开始扫描和结束扫描的事件
int ev_cnt_;
SANE_FinalImgFormat img_fmt_;
bool twain_set_;
SANEAPI sane_api_;
std::map<sane_option_id, int> sane_ids_; // <fix-id, sane-option-sn>
std::function<void(int, void*, int)> ui_notify;
int(* scanner_ev_handler_)(int, void*);
void* evh_param_;
HWND app_wnd_; // for MessageBox
bool is_show_ui_;
int transfer_id(int id); // transfer fixed SANE option ID to real id, -1 is none
void transport_config_file(void);
void update_config(void);
void load_config(const wchar_t* file);
void save_config(const wchar_t* file);
void apply_config(void);
void on_ui_event(int uev, void* sender);
std::string choose_scanner(const std::vector<std::string>& scanners);
int open(void);
int close(void);
int init_options_id(void);
char* get_opt_value(int id, int size, bool def_val/*false - current value*/); // call delete[] to free returned value
bool get_opt_value(int id, void* buf, bool def_val/*false - current value*/); // call delete[] to free returned value
int control_read_string(int io_code, std::string& val);
void extension_none(int id);
void extension_multiout_type(int id);
void extension_color_mode(int id);
void extension_sharpen(int id);
void extension_paper(int id);
void extension_fill_bkg_method(int id);
void extension_text_direction(int id);
void extension_page(int id);
void extension_erase_color(int id);
bool get_option_value_with_parent(int sn, set_opt_value setv, void* param); // return true if handled
bool set_option_value_with_parent(int sn, void* data, int* err); // return true if handled sn
int set_option_value(int sn, SANE_Value_Type type, int size, void* data);
int set_is_multiout(bool enable);
int thread_start(void);
std::unique_ptr<std::thread> thread_starting_;
typedef struct _ex_api
{
unsigned int ind;
unsigned int base_ind;
int(scanner::* ex_api)(int, void*, set_opt_value);
bool operator==(const int& id)
{
return ind == id;
}
}EXAPI;
std::vector<EXAPI> ex_opts_;
typedef std::vector<EXAPI>::iterator EXAPIPOS;
EXAPIPOS find_ex_api(int op_id);
;
EX_OPTION_HANDLER_DECL(multiout);
EX_OPTION_HANDLER_DECL(auto_color_type);
EX_OPTION_HANDLER_DECL(color_mode);
EX_OPTION_HANDLER_DECL(sharpen); // int
EX_OPTION_HANDLER_DECL(paper);
EX_OPTION_HANDLER_DECL(paper_lateral);
EX_OPTION_HANDLER_DECL(auto_paper_size);
EX_OPTION_HANDLER_DECL(auto_paper_crop);
EX_OPTION_HANDLER_DECL(text_direction);
EX_OPTION_HANDLER_DECL(duplex);
EX_OPTION_HANDLER_DECL(fill_background); // bool true - 凸多边形
EX_OPTION_HANDLER_DECL(discard_blank_page);
EX_OPTION_HANDLER_DECL(discard_blank_receipt);
EX_OPTION_HANDLER_DECL(page_fold);
EX_OPTION_HANDLER_DECL(color_filter);
EX_OPTION_HANDLER_DECL(color_enhance);
EX_OPTION_HANDLER_DECL(final_compression); // int
EX_OPTION_HANDLER_DECL(final_format); // SANE_FinalImgFormat
EX_OPTION_HANDLER_DECL(serial); // std::string
EX_OPTION_HANDLER_DECL(to_be_scan); // bool
EX_OPTION_HANDLER_DECL(scan_with_hole); // bool
EX_OPTION_HANDLER_DECL(device_code); // std::string
EX_OPTION_HANDLER_DECL(power); // int
EX_OPTION_HANDLER_DECL(hardware_version); // std::string
EX_OPTION_HANDLER_DECL(ip); // std::string
EX_OPTION_HANDLER_DECL(erase_hole);
EX_OPTION_HANDLER_DECL(search_hole_range);
template<class T>
bool set_cur_and_def_value(T cur, T def, set_opt_value setv, void* param)
{
if (cur == def)
return setv(&cur, (value_role)(VAL_ROLE_CURRENT | VAL_ROLE_DEFAULT), VAL_LIMIT_NONE, param);
else if (setv(&cur, VAL_ROLE_CURRENT, VAL_LIMIT_NONE, param))
return setv(&def, VAL_ROLE_DEFAULT, VAL_LIMIT_NONE, param);
return false;
}
template<class S, class T>
void set_value_range(S cur, S def, S l, S u, S s, set_opt_value setv, void* param, T(* to_t)(S))
{
T v = to_t(cur);
while (setv(&v, VAL_ROLE_CURRENT, VAL_LIMIT_RANGE, param))
{
v = to_t(def);
if (!setv(&v, VAL_ROLE_DEFAULT, VAL_LIMIT_RANGE, param))
break;
v = to_t(l);
if (!setv(&v, VAL_ROLE_LOWER, VAL_LIMIT_RANGE, param))
break;
v = to_t(u);
if (!setv(&v, VAL_ROLE_UPPER, VAL_LIMIT_RANGE, param))
break;
v = to_t(s);
setv(&v, VAL_ROLE_STEP, VAL_LIMIT_RANGE, param);
break;
}
}
static int to_int(SANE_Int v);
static float to_float(SANE_Fixed v);
static void ui_callback(int uev, void* sender, void* param);
static bool is_option_float(int sn, void* param);
public:
scanner(SCANNERID id);
protected:
~scanner();
public:
static bool is_belong_serial(int vid, int pid, SCANNERID serial);
static void get_scanner_name(SCANNERID id, std::vector<std::string>& names);
static value_type from_sane_type(SANE_Value_Type type);
static value_limit from_sane_constraint(SANE_Constraint_Type type);
// IRef
public:
COM_API_OVERRIDE(long, add_ref(void));
COM_API_OVERRIDE(long, release(void));
// ISaneInvoker
public:
COM_API_OVERRIDE(int, start(void));
COM_API_OVERRIDE(int, stop(void));
COM_API_OVERRIDE(int, get_event(void));
COM_API_OVERRIDE(void, set_event_callback(int(* handle_ev)(int, void*) = NULL, void* para = NULL));
COM_API_OVERRIDE(bool, wait_image(DWORD milliseconds = -1));
COM_API_OVERRIDE(int, get_scanned_images(DWORD milliseconds = 0));
COM_API_OVERRIDE(IScanImg*, take_first_image(twain_xfer xfer = TWAIN_XFER_Native)); // call 'release' on returned value, plz
COM_API_OVERRIDE(bool, get_first_image_header(SANE_Parameters* header, size_t* bytes = NULL, int* dpi = NULL));
COM_API_OVERRIDE(bool, is_online(void));
COM_API_OVERRIDE(bool, is_paper_on(void));
COM_API_OVERRIDE(int, last_error(void));
COM_API_OVERRIDE(int, image_fetched(IScanImg* tx));
COM_API_OVERRIDE(bool, get_option_info(int sn, value_type* type, value_limit* limit, int* bytes));
COM_API_OVERRIDE(bool, get_value(int sn, set_opt_value, void* param));
COM_API_OVERRIDE(bool, get_value(int sn, void* data, int* len)); // get operation with in-parameter
COM_API_OVERRIDE(int, set_value(int sn, void* val));
COM_API_OVERRIDE(int, convert_image(SANE_ImageFormatConvert* conv));
COM_API_OVERRIDE(void, free_buffer(void* buf, int len));
COM_API_OVERRIDE(int, get_fixed_ids(bool(* cb)(uint32_t id, void* param), void* param));
// SANE options ID ...
SANE_OPTION_ID(color_correction); // 2023-02-24 15:31:19 色偏校正
SANE_OPTION_ID(fold_type); // 2023-02-24 15:28:46 对折模式
SANE_OPTION_ID(is_multiout);
SANE_OPTION_ID(multiout_type);
SANE_OPTION_ID(color_mode);
SANE_OPTION_ID(erase_color);
SANE_OPTION_ID(erase_multiout_red);
SANE_OPTION_ID(erase_paper_red);
SANE_OPTION_ID(is_erase_background);
SANE_OPTION_ID(background_color_range);
SANE_OPTION_ID(sharpen);
SANE_OPTION_ID(erase_morr);
SANE_OPTION_ID(erase_grids); // 除网纹
SANE_OPTION_ID(error_extend);
SANE_OPTION_ID(is_noise_modify);
SANE_OPTION_ID(noise_threshold);
SANE_OPTION_ID(paper);
SANE_OPTION_ID(is_custom_area);
SANE_OPTION_ID(curstom_area_l);
SANE_OPTION_ID(curstom_area_r);
SANE_OPTION_ID(curstom_area_t);
SANE_OPTION_ID(curstom_area_b);
SANE_OPTION_ID(is_size_check);
SANE_OPTION_ID(page);
SANE_OPTION_ID(blank_page_threshold); // 跳过空白页灵敏度
SANE_OPTION_ID(resolution);
SANE_OPTION_ID(image_quality);
SANE_OPTION_ID(is_swap); // 交换正反面
SANE_OPTION_ID(is_split); // 图像拆分
SANE_OPTION_ID(is_auto_deskew); // 自动纠偏
SANE_OPTION_ID(is_custom_gamma);
SANE_OPTION_ID(bright);
SANE_OPTION_ID(contrast);
SANE_OPTION_ID(gamma);
SANE_OPTION_ID(is_erase_black_frame); // bool
SANE_OPTION_ID(deep_sample);
SANE_OPTION_ID(threshold);
SANE_OPTION_ID(anti_noise); // 抗噪等级
SANE_OPTION_ID(margin);
SANE_OPTION_ID(fill_background);
SANE_OPTION_ID(is_anti_permeate);
SANE_OPTION_ID(anti_permeate_level);
SANE_OPTION_ID(is_erase_hole);
SANE_OPTION_ID(search_hole_range);
SANE_OPTION_ID(is_filling_color); // 色彩填充
SANE_OPTION_ID(is_ultrasonic_check);
SANE_OPTION_ID(is_check_staple);
SANE_OPTION_ID(scan_mode); // 扫描张数
SANE_OPTION_ID(scan_count); // 扫描数量
SANE_OPTION_ID(text_direction);
SANE_OPTION_ID(is_rotate_bkg180);
SANE_OPTION_ID(is_check_dogear);
SANE_OPTION_ID(dogear_size);
SANE_OPTION_ID(is_check_skew);
SANE_OPTION_ID(skew_range);
SANE_OPTION_ID(black_white_threshold); // 二值化图像阈值
SANE_OPTION_ID(is_photo_mode); // 照片模式
SANE_OPTION_ID(double_feed_handle); // 双张图片处理
SANE_OPTION_ID(scan_when_paper_on); // 待纸扫描
SANE_OPTION_ID(feed_strength); // 分纸强度
SANE_OPTION_ID(power_scheme); // 休眠时间
SANE_OPTION_ID(is_auto_strength); // 自动搓纸强度
SANE_OPTION_ID(feed_strength_value); // 自动搓纸强度设定值
SANE_OPTION_ID(is_reverse_bw); // 黑白图像反色输出
SANE_OPTION_ID(is_erase_hole_l); // 穿孔移除 - 左
SANE_OPTION_ID(search_hole_range_l); // 穿孔搜索范围 - 左
SANE_OPTION_ID(is_erase_hole_r); // 穿孔移除 - 右
SANE_OPTION_ID(search_hole_range_r); // 穿孔搜索范围 - 右
SANE_OPTION_ID(is_erase_hole_t); // 穿孔移除 - 上
SANE_OPTION_ID(search_hole_range_t); // 穿孔搜索范围 - 上
SANE_OPTION_ID(is_erase_hole_b); // 穿孔移除 - 下
SANE_OPTION_ID(search_hole_range_b); // 穿孔搜索范围 - 下
SANE_OPTION_ID(fold_direction); // 对折模式
// SANE-ex option ID:
SANE_OPTION_ID(ex_multiout_type); // int
SANE_OPTION_ID(ex_auto_color_type); // int
SANE_OPTION_ID(ex_color_mode); // int
SANE_OPTION_ID(ex_sharpen); // int
SANE_OPTION_ID(ex_paper); // paper_value
SANE_OPTION_ID(ex_paper_lateral); // bool
SANE_OPTION_ID(ex_auto_paper_size); // bool
SANE_OPTION_ID(ex_is_paper_auto_crop); // bool
SANE_OPTION_ID(ex_text_direction); // float 90, 180, ..., -1 is auto-text-direction
SANE_OPTION_ID(ex_duplex); // bool
SANE_OPTION_ID(ex_fill_background); // bool true - 凸多边形
SANE_OPTION_ID(ex_discard_blank_page); // bool
SANE_OPTION_ID(ex_discard_blank_receipt); // bool
SANE_OPTION_ID(ex_is_page_fold); // bool
SANE_OPTION_ID(ex_color_filter); // int (filter_value)
SANE_OPTION_ID(ex_color_enhance); // int (enhance_value)
SANE_OPTION_ID(ex_final_compression); // int
SANE_OPTION_ID(ex_final_format); // SANE_FinalImgFormat
SANE_OPTION_ID(ex_serial); // std::string
SANE_OPTION_ID(ex_to_be_scan); // bool
SANE_OPTION_ID(ex_scan_with_hole); // bool
SANE_OPTION_ID(ex_device_code); // std::string
SANE_OPTION_ID(ex_power); // int
SANE_OPTION_ID(ex_hardware_version); // std::string
SANE_OPTION_ID(ex_ip); // std::string
int language_id_;
// ui ...
COM_API_OVERRIDE(bool, ui_show_main(HWND parent));
COM_API_OVERRIDE(bool, ui_show_setting(HWND parent, bool with_scan, bool indicator = true));
COM_API_OVERRIDE(bool, ui_show_progress(HWND parent, bool bIndicator));
COM_API_OVERRIDE(void, ui_hide(void));
COM_API_OVERRIDE(bool, ui_is_ok(void));
// twain
COM_API_OVERRIDE(void, twain_set_transfer(twain_xfer xfer));
COM_API_OVERRIDE(void, twain_set_compression(SANE_CompressionType compression, void* detail = NULL));
COM_API_OVERRIDE(int, twain_get_config(char* buf, size_t* len));
COM_API_OVERRIDE(int, twain_set_config(char* buf, size_t len));
// methods:
public:
int handle_device_event(int ev_code, void* data, unsigned int* len);
};

920
twain/utils.cpp Normal file
View File

@ -0,0 +1,920 @@
#include "utils.h"
#include "huagao/brand.h"
#include <mutex>
#include <algorithm>
#if OS_WIN
#include <direct.h>
#include <Windows.h>
#include <time.h>
#include <Psapi.h>
#include <Tlhelp32.h>
#pragma comment(lib, "Psapi.lib")
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 // microseconds from '1601-01-01 00:00:00' to '1970-01-01 00:00:00'
int gettimeofday(TIMEV* tv, struct timezone* tz)
{
FILETIME ft = { 0 };
uint64_t ui64 = 0;
static bool set_tz = true;
GetSystemTimeAsFileTime(&ft); // 100 ns - from 1601-01-01 00:00:00
ui64 = ft.dwHighDateTime;
ui64 <<= 32;
ui64 |= ft.dwLowDateTime;
// convert to microseconds ...
ui64 += 5;
ui64 /= 10;
// move to 1970-01-01 00:00:00
ui64 -= DELTA_EPOCH_IN_MICROSECS;
if (tv)
{
tv->tv_sec = ui64 / 1000000;
tv->tv_usec = ui64 % 1000000;
}
if (tz)
{
if (set_tz)
{
set_tz = false;
_tzset();
}
tz->tz_minuteswest = _timezone / 60;
tz->tz_dsttime = _daylight;
}
return 0;
}
#else
#include "ini_file.h"
#include <map>
#include <sys/sysinfo.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <thread>
#include <iconv.h>
static std::mutex ini_lock_;
static std::map<std::string, simple_ini*> ini_files_;
static simple_ini* get_ini_object(const char* file)
{
std::lock_guard<std::mutex> lock(ini_lock_);
if (ini_files_.count(file) == 0)
{
simple_ini* ini = new simple_ini();
ini->load(file);
ini_files_[file] = ini;
}
return ini_files_[file];
}
DWORD GetPrivateProfileStringA(const char* lpAppName, const char* lpKeyName, const char* lpDefault, char* lpReturnedString, DWORD nSize, const char* lpFileName)
{
simple_ini* ini = get_ini_object(lpFileName);
std::string str(ini->get(lpAppName, lpKeyName, lpDefault));
if (nSize)
{
if (nSize < str.length())
{
memcpy(lpReturnedString, str.c_str(), nSize);
}
else
{
strcpy(lpReturnedString, str.c_str());
nSize = str.length();
}
}
return nSize;
}
BOOL WritePrivateProfileStringA(const char* lpAppName, const char* lpKeyName, const char* lpString, const char* lpFileName)
{
simple_ini* ini = get_ini_object(lpFileName);
ini->set(lpAppName, lpKeyName, lpString);
ini->save(lpFileName);
return TRUE;
}
DWORD GetLastError(void)
{
return errno;
}
DWORD GetPrivateProfileIntA(const char* app, const char* key, DWORD def, const char* file)
{
std::string val(get_ini_object(file)->get(app, key));
return val.empty() ? def : atoi(val.c_str());
}
DWORD GetPrivateProfileStringA(const char* app, const char* key, const char* init, char* buf, size_t len, const char* file)
{
std::string val(get_ini_object(file)->get(app, key));
if(val.empty())
{
if(init)
{
strcpy(buf, init);
len = strlen(init);
}
else
{
len = 0;
}
}
else
{
if(len < val.length())
memcpy(buf, val.c_str(), len);
else
{
strcpy(buf, val.c_str());
len = val.length();
}
}
return len;
}
void Sleep(DWORD milliseconds)
{
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
}
int GetModuleFileNameA(HMODULE module, char* buf, size_t len)
{
std::string name(""),
val(utils::get_module_full_path((char*)module));
val += PATH_SEPARATOR + name;
if(len < val.length())
memcpy(buf, val.c_str(), len);
else
{
strcpy(buf, val.c_str());
len = val.length();
}
return len;
}
int GetCurrentThreadId(void)
{
return pthread_self();
}
int GetCurrentProcessId(void)
{
return getpid();
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// log class
#define MAX_LOG_FILE_SIZE SIZE_MB(10)
class log_cls
{
void(*log_)(const char*, void*, void*);
std::string path_file_;
FILE* file_;
int level_;
int type_;
std::mutex lock_;
static log_cls* inst_;
static FILE* create_log_file(const char* path_file, bool truncate)
{
FILE* file_ = fopen(path_file, "a+b");
if (file_)
{
fseek(file_, 0, SEEK_END);
if (ftell(file_) == 0)
{
unsigned char bom[] = { 0x0ef, 0x0bb, 0x0bf };
fwrite(bom, sizeof(bom), 1, file_);
}
else
{
std::string sep("\n\n===================================================================================================================\n");
fwrite(sep.c_str(), sizeof(sep[0]), sep.length(), file_);
}
std::string now("[" + utils::format_current_time() + "]: =====================================");
now += truncate ? "--truncated--=====================================\n" : "--started--=====================================\n";
fwrite(now.c_str(), sizeof(now[0]), now.length(), file_);
}
return file_;
}
static void log_none(const char* info, void* param, void* param2)
{}
static void log_consonle(const char* info, void* param, void* param2)
{
printf(info);
}
static void log_file(const char* info, void* param, void* param2)
{
FILE** file = (FILE**)param;
if (*file == nullptr)
*file = create_log_file(((std::string*)param2)->c_str(), false);
if (*file)
{
fwrite(info, 1, strlen(info), *file);
fflush(*file);
if (ftell(*file) >= MAX_LOG_FILE_SIZE)
{
fclose(*file);
remove(((std::string*)param2)->c_str());
*file = create_log_file(((std::string*)param2)->c_str(), true);
}
}
}
protected:
log_cls() : path_file_(""), file_(0), log_(&log_cls::log_consonle), level_(LOG_LEVEL_ALL)
{}
~log_cls()
{
if (file_)
{
fclose(file_);
file_ = 0;
}
}
public:
static log_cls* instance(void)
{
if (!log_cls::inst_)
log_cls::inst_ = new log_cls();
return log_cls::inst_;
}
int set_log_type(int type, void* param)
{
int ret = 0;
if (file_)
{
fclose(file_);
file_ = 0;
}
type_ = type;
if (type == LOG_TYPE_NONE)
log_ = &log_cls::log_none;
else if (type == LOG_TYPE_CONSOLE)
log_ = &log_cls::log_consonle;
else if (type == LOG_TYPE_FILE)
{
log_ = &log_cls::log_file;
ret = -1;
if (param)
{
path_file_ = (char*)param;
file_ = create_log_file(path_file_.c_str(), false);
if (file_)
ret = 0;
}
}
if (ret != 0)
{
log_ = &log_cls::log_none;
type_ = LOG_TYPE_NONE;
}
return ret;
}
void set_log_level(int level)
{
level_ = level;
}
int level(void)
{
return level_;
}
int type(void)
{
return type_;
}
void log(const char* info)
{
std::lock_guard<std::mutex> lock(lock_);
log_(info, &file_, &path_file_);
}
std::string get_log_file_path(bool copy)
{
std::string file("");
if (log_ == &log_cls::log_file && file_)
{
file = path_file_;
if (copy)
{
file += ".txt";
FILE* dst = fopen(file.c_str(), "wb");
if (!dst)
file = "";
else
{
std::lock_guard<std::mutex> lock(lock_);
char buf[1024] = { 0 };
size_t l = 0;
fseek(file_, 0, SEEK_SET);
while ((l = fread(buf, 1, sizeof(buf), file_)))
fwrite(buf, 1, l, dst);
fclose(dst);
}
}
}
return file;
}
void clear_log(void)
{
if (log_ == &log_cls::log_file && file_)
{
std::lock_guard<std::mutex> lock(lock_);
fclose(file_);
remove(path_file_.c_str());
file_ = create_log_file(path_file_.c_str(), true);
}
}
};
log_cls* log_cls::inst_ = NULL;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// util
namespace utils
{
static bool STDCALL match_part_filename(const char* path_name, bool dir, void* param)
{
std::string* partn = (std::string*)param;
if (!dir)
{
if (partn[0].empty())
{
partn[1] = path_name;
}
else
{
const char* name = strrchr(path_name, PATH_SEPARATOR[0]);
if (name++ == nullptr)
name = path_name;
std::string n(name);
to_lower(n);
if (strstr(n.c_str(), partn[0].c_str()))
partn[1] = path_name;
else
dir = true;
}
}
return dir;
}
#if OS_WIN
static std::string u2m(const wchar_t* u, int page)
{
char* ansi = NULL;
int len = 0;
std::string mb("");
len = WideCharToMultiByte(page, 0, u, lstrlenW(u), NULL, 0, NULL, NULL);
ansi = new char[len + 2];
len = WideCharToMultiByte(page, 0, u, lstrlenW(u), ansi, len, NULL, NULL);
ansi[len--] = 0;
mb = ansi;
delete[] ansi;
return mb;
}
static std::wstring m2u(const char* m, int page)
{
wchar_t* unic = NULL;
int len = 0;
std::wstring u(L"");
len = MultiByteToWideChar(page, 0, m, lstrlenA(m), NULL, 0);
unic = new wchar_t[len + 2];
len = MultiByteToWideChar(page, 0, m, lstrlenA(m), unic, len);
unic[len--] = 0;
u = unic;
delete[] unic;
return u;
}
std::string utf82ansi(const char* utf8)
{
return u2m(m2u(utf8, CP_UTF8).c_str(), CP_ACP);
}
std::string ansi2utf8(const char* ansi)
{
return u2m(m2u(ansi, CP_ACP).c_str(), CP_UTF8);
}
#else
// This function will return 'in' string if failed !
static std::string transform_between_gbk_and_utf8(const char* in, bool to_utf8, int *err)
{
size_t len = strlen(in) + 8, ol = len * 2;
char *buf = (char*)malloc(len), *oper = buf, *out = nullptr, *oper1 = nullptr;
iconv_t conv;
memset(buf, 0, len);
strcpy(buf, in);
if(to_utf8)
conv = iconv_open("UTF-8", "GBK");
else
conv = iconv_open("GBK", "UTF-8");
if(conv == (iconv_t)-1)
{
if(err)
*err = errno;
free(buf);
return in;
}
oper1 = out = (char*)malloc(ol);
memset(out, 0, ol);
len -= 8;
if(iconv(conv, &oper, &len, &oper1, &ol))
{
if(err)
*err = errno;
}
else if(err)
*err = 0;
std::string ret(out);
free(buf);
free(out);
iconv_close(conv);
return ret.empty() ? in : std::move(ret);
}
std::string utf82ansi(const char* utf8)
{
// fix me ...
return transform_between_gbk_and_utf8(utf8, false, nullptr);
}
std::string ansi2utf8(const char* ansi)
{
// fix me ...
return transform_between_gbk_and_utf8(ansi, true, nullptr);
}
#endif
std::string get_command_result(const char* cmd, int len)
{
std::string result("");
#if OS_WIN
#else
FILE* src = popen(cmd, "r");
if (src)
{
char buf[128] = { 0 };
int rv = fread(buf, 1, sizeof(buf) - 1, src);
while (rv > 0)
{
buf[rv] = 0;
result += buf;
if (len != -1 && result.length() >= len)
{
result.erase(len);
break;
}
rv = fread(buf, 1, sizeof(buf) - 1, src);
}
pclose(src);
}
#endif
return std::move(result);
}
std::string get_local_data_path(void)
{
static std::string ldp("");
if (ldp.empty())
{
#if OS_WIN
const char* path(getenv("LOCALAPPDATA"));
if (path)
{
ldp = path;
ldp += PATH_SEPARATOR;
}
#else
const char* path(getenv("HOME"));
if (path)
{
if (strstr(path, "/root"))
{
std::string usr(get_command_result("logname"));
ldp = std::string("/home/") + trim(usr);
if (!opendir(ldp.c_str()))
{
printf("opendir(%s) failed: %s\n", ldp.c_str(), strerror(errno));
ldp = path;
}
}
else
{
ldp = path;
}
ldp += std::string(PATH_SEPARATOR) + ".";
}
#endif
ldp += PRODUCT_VENDOR;
ldp += "Scan";
create_folder(ldp.c_str());
printf("# local data path: %s\n", ldp.c_str());
}
return ldp;
}
std::string format_current_time(void)
{
return std::move(chronograph::now());
}
std::string get_module_full_path(const char* part_name/*nullptr to get main-pe/first module's full path*/)
{
std::string file[] = { part_name ? part_name : "", ""};
to_lower(file[0]);
#if OS_WIN
if (part_name && *part_name)
{
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
MODULEENTRY32W pei = { 0 };
if (h != INVALID_HANDLE_VALUE)
{
pei.dwSize = sizeof(pei);
if (Module32FirstW(h, &pei))
{
do
{
char path[256] = { 0 };
GetModuleFileNameA(pei.hModule, path, _countof(path) - 1);
if (!match_part_filename(path, false, (void*)file))
break;
pei.dwSize = sizeof(pei);
} while (Module32NextW(h, &pei));
}
CloseHandle(h);
}
}
else
{
char path[256] = { 0 };
GetModuleFileNameA(NULL, path, _countof(path) - 1);
file[1] = path;
}
#else
char path[128] = { 0 };
sprintf(path, "/proc/%u/map_files/", getpid());
enum_file(path, false, match_part_filename, (void*)file);
#endif
return std::move(file[1]);
}
std::string target_file_from_link(const char* lnk_file)
{
#if OS_WIN
std::string ret("");
return ret;
#else
char path[256] = { 0 };
int len = readlink(lnk_file, path, sizeof(path) - 1);
return len > 0 ? path : lnk_file;
#endif
}
std::string get_ini_value(const char* seg, const char* key, const char* cfg_file)
{
char buf[512] = { 0 };
GetPrivateProfileStringA(seg, key, "", buf, sizeof(buf) - 1, cfg_file);
return buf;
}
const char* to_lower(std::string& str)
{
std::transform(str.begin(), str.end(), str.begin(), tolower);
return str.c_str();
}
const char* trim(std::string& str, const char* sp)
{
int pos = 0;
char ch[2] = { 0 };
for (; pos < str.length(); ++pos)
{
ch[0] = str[pos];
if (!strstr(sp, ch))
break;
}
if (pos)
str.erase(0, pos);
pos = str.length() - 1;
for (; pos >= 0; --pos)
{
ch[0] = str[pos];
if (!strstr(sp, ch))
break;
}
if (++pos < str.length())
str.erase(pos);
return str.c_str();
}
bool create_folder(const char* folder)
{
int ret = MKDIR(folder, S_IREAD | S_IWRITE | S_IEXEC);
return ret == 0 || errno == EEXIST;
}
void set_ini_value(const char* seg, const char* key, const char* val, const char* cfg_file)
{
WritePrivateProfileStringA(seg, key, val, cfg_file);
}
int enum_file(const char* folder, bool recursive, bool(STDCALL* found)(const char* path_name, bool dir, void* param), void* param)
{
int ret = EACCES;
#if OS_WIN
WIN32_FIND_DATAA fd = { 0 };
HANDLE hf = FindFirstFileA(folder, &fd);
if (hf == INVALID_HANDLE_VALUE)
ret = GetLastError();
else
{
do
{
bool is_dir = (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY;
if (!found(fd.cFileName, is_dir, param))
{
ret = ERROR_CANCELLED;
break;
}
if (is_dir && recursive)
{
char* name = strrchr(fd.cFileName, PATH_SEPARATOR[0]);
if (name++ == NULL)
name = fd.cFileName;
if (strcmp(name, ".") && strcmp(name, ".."))
{
if (enum_file(fd.cFileName, recursive, found, param) == ERROR_CANCELLED)
{
ret = ERROR_CANCELLED;
break;
}
}
}
} while (FindNextFileA(hf, &fd));
FindClose(hf);
}
#else
DIR* pdir = nullptr;
struct dirent* ent = nullptr;
pdir = opendir(folder);
if (!pdir)
ret = errno;
else
{
while ((ent = readdir(pdir)))
{
std::string file(folder);
file += PATH_SEPARATOR;
file += ent->d_name;
if (!found(target_file_from_link(file.c_str()).c_str(), ent->d_type & DT_DIR, param))
{
ret = ERROR_CANCELLED;
break;
}
if (ent->d_type & DT_DIR)
{
if (recursive)
{
if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, ".."))
{
std::string sub(folder);
sub += PATH_SEPARATOR;
sub += ent->d_name;
if (enum_file(sub.c_str(), recursive, found, param) == ERROR_CANCELLED)
{
ret = ERROR_CANCELLED;
break;
}
}
}
}
}
}
#endif
return ret;
}
int move_file(const char* from, const char* to)
{
return rename(from, to);
}
void init_log(log_type type, log_level level, const char* fn_appendix)
{
std::string file("");
if (type == LOG_TYPE_FILE)
{
file = get_local_data_path() + PATH_SEPARATOR + "Log";
create_folder(file.c_str());
std::string pe(get_module_full_path());
size_t pos = pe.rfind(PATH_SEPARATOR[0]);
if (pos++ == std::string::npos)
pos = 0;
file += PATH_SEPARATOR + pe.substr(pos);
if (fn_appendix)
file += fn_appendix;
file += ".log";
}
log_cls::instance()->set_log_type(type, &file[0]);
log_cls::instance()->set_log_level(level);
}
void log_info(const char* info, int level)
{
if (get_log_type() != LOG_TYPE_NONE && get_log_level() <= level)
{
log_cls::instance()->log(("[" + format_current_time() + "]: " + info).c_str());
}
}
int get_log_type(void)
{
return log_cls::instance()->type();
}
int get_log_level(void)
{
return log_cls::instance()->level();
}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// util
chronograph::chronograph()
{
reset();
}
chronograph::~chronograph()
{}
bool chronograph::now(TIMEV* tv)
{
struct timezone tz = { 0 };
return gettimeofday(tv, &tz) == 0;
}
bool chronograph::now(uint64_t* seconds, uint64_t* u_seconds)
{
TIMEV tv = { 0 };
struct timezone tz = { 0 };
if (gettimeofday(&tv, &tz) == 0)
{
if (seconds)
*seconds = tv.tv_sec;
if (u_seconds)
*u_seconds = tv.tv_usec;
return true;
}
else
{
return false;
}
}
std::string chronograph::now(bool with_ms/*whether with milliseconds*/) // return '2022-11-30 10:38:42.123', no '.123' if with_ms was false
{
TIMEV tv = { 0 };
if (!chronograph::now(&tv))
return "";
char buf[40] = { 0 };
time_t t = tv.tv_sec;
struct tm* l = localtime(&t);
if (with_ms)
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d.%06d", l->tm_year + 1900, l->tm_mon + 1, l->tm_mday
, l->tm_hour, l->tm_min, l->tm_sec, tv.tv_usec);
else
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d", l->tm_year + 1900, l->tm_mon + 1, l->tm_mday
, l->tm_hour, l->tm_min, l->tm_sec);
return buf;
}
uint64_t chronograph::elapse_s(void)
{
TIMEV tv = { 0 };
chronograph::now(&tv);
return tv.tv_sec - bgn_.tv_sec;
}
uint64_t chronograph::elapse_ms(void)
{
TIMEV tv = { 0 };
uint64_t dif = 0;
chronograph::now(&tv);
dif = SEC_2_MS(tv.tv_sec - bgn_.tv_sec);
dif += tv.tv_usec / MSEC_2_US(1);
dif -= bgn_.tv_usec / MSEC_2_US(1);
return dif;
}
uint64_t chronograph::elapse_us(void)
{
TIMEV tv = { 0 };
uint64_t dif = 0;
chronograph::now(&tv);
dif = SEC_2_US(tv.tv_sec - bgn_.tv_sec);
dif += tv.tv_usec;
dif -= bgn_.tv_usec;
return dif;
}
void chronograph::reset()
{
chronograph::now(&bgn_);
}

102
twain/utils.h Normal file
View File

@ -0,0 +1,102 @@
// utilities for platform ...
//
// Date: 2023-06-30
//
#pragma once
#include <sane/sane_ex.h>
#include <string>
#include <memory>
enum log_type
{
LOG_TYPE_NONE = 0, // no logging
LOG_TYPE_CONSOLE, // print to console
LOG_TYPE_FILE, // write log into file
};
enum log_level
{
LOG_LEVEL_ALL = 0,
LOG_LEVEL_DEBUG,
LOG_LEVEL_WARNING,
LOG_LEVEL_FATAL,
};
namespace utils
{
std::string utf82ansi(const char* utf8);
std::string ansi2utf8(const char* ansi);
std::string get_command_result(const char* cmd, int len = -1);
std::string get_local_data_path(void);
std::string format_current_time(void);
std::string get_module_full_path(const char* part_name = nullptr/*nullptr to get main-pe/first module's full path*/);
std::string target_file_from_link(const char* lnk_file);
std::string get_ini_value(const char* seg, const char* key, const char* cfg_file); // return "" if not found
const char* to_lower(std::string& str); // return str.c_str()
const char* trim(std::string& str, const char* sp = "\r\n\t "); // return str.c_str()
bool create_folder(const char* folder);
void set_ini_value(const char* seg, const char* key, const char* val, const char* cfg_file);
int enum_file(const char* folder, bool recursive, bool/*return false to stop enumeration*/(STDCALL* found)(const char* path_name, bool dir, void* param), void* param);
int move_file(const char* from, const char* to);
void init_log(log_type type, log_level level = LOG_LEVEL_ALL, const char* fn_appendix = nullptr/*appendix to default log-file-name*/);
void log_info(const char* info, int level = LOG_LEVEL_ALL);
int get_log_type(void);
int get_log_level(void);
template<typename ... Args>
void to_log(int level, const char* fmt, Args ... args)
{
if (get_log_type() != LOG_TYPE_NONE && get_log_level() <= level)
{
size_t size = snprintf(nullptr, 0, fmt, args ...) + 2;
std::unique_ptr<char[]> buf(new char[size]);
snprintf(buf.get(), size, fmt, args ...);
log_info(buf.get(), (log_level)level);
}
}
};
#if OS_WIN
struct _time_val
{
time_t tv_sec; /* Seconds. */
time_t tv_usec; /* Microseconds. */
};
typedef struct _time_val TIMEV;
struct timezone
{
int tz_minuteswest; /* Minutes west of GMT. */
int tz_dsttime; /* Nonzero if DST is ever in effect. */
};
int gettimeofday(TIMEV* tv, struct timezone* tz);
#else
#include <sys/time.h>
typedef struct timeval TIMEV;
#endif
class chronograph
{
TIMEV bgn_;
public:
chronograph();
~chronograph();
static bool now(TIMEV* tv);
static bool now(uint64_t* seconds, uint64_t* u_seconds);
static std::string now(bool with_ms = true/*whether with milliseconds*/); // return '2022-11-30 10:38:42.123', no '.123' if with_ms was false
public:
uint64_t elapse_s(void);
uint64_t elapse_ms(void);
uint64_t elapse_us(void);
void reset(void);
};