#include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "base/HGUtility.h" struct { const char* name; const char* title; }g_opts[] = { {SANE_STD_OPT_NAME_RESTORE , OPTION_TITLE_HFMRSZ} , {SANE_STD_OPT_NAME_HELP , OPTION_TITLE_BZ} , {SANE_STD_OPT_NAME_IS_MULTI_OUT , OPTION_TITLE_DLSC} , {SANE_STD_OPT_NAME_MULTI_OUT_TYPE , OPTION_TITLE_DLSCLX} , {SANE_STD_OPT_NAME_COLOR_MODE , OPTION_TITLE_YSMS} , {SANE_STD_OPT_NAME_BINARY_THRESHOLD , OPTION_TITLE_HBTXYZ} , {SANE_STD_OPT_NAME_REVERSE_01 , OPTION_TITLE_HBTXFSSC} , {SANE_STD_OPT_NAME_FILTER , OPTION_TITLE_HDHHBTX_CSYZQ} , {SANE_STD_OPT_NAME_RID_MULTIOUT_RED , OPTION_TITLE_24WCSTX_DLSCCH} , {SANE_STD_OPT_NAME_RID_ANSWER_SHEET_RED , OPTION_TITLE_24WCSTX_DTKCH} , {SANE_STD_OPT_NAME_ERASE_BACKGROUND , OPTION_TITLE_BJYC} , {SANE_STD_OPT_NAME_BKG_COLOR_RANGE , OPTION_TITLE_BJSCFDFW} , {SANE_STD_OPT_NAME_SHARPEN , OPTION_TITLE_RHYMH} , {SANE_STD_OPT_NAME_RID_MORR , OPTION_TITLE_QCMW} , {SANE_STD_OPT_NAME_RID_GRID , OPTION_TITLE_CWW} , {SANE_STD_OPT_NAME_ERROR_EXTENSION , OPTION_TITLE_CWKS} , {SANE_STD_OPT_NAME_NOISE_OPTIMIZE , OPTION_TITLE_HBTXZDYH} , {SANE_STD_OPT_NAME_NOISE_SIZE , OPTION_TITLE_ZDYHCC} , {SANE_STD_OPT_NAME_PAPER , OPTION_TITLE_ZZCC} , {SANE_STD_OPT_NAME_CUSTOM_AREA , OPTION_TITLE_ZDYSMQY} , {SANE_STD_OPT_NAME_CUSTOM_AREA_LEFT , OPTION_TITLE_SMQYZCmm} , {SANE_STD_OPT_NAME_CUSTOM_AREA_RIGHT , OPTION_TITLE_SMQYYCmm} , {SANE_STD_OPT_NAME_CUSTOM_AREA_TOP , OPTION_TITLE_SMQYSCmm} , {SANE_STD_OPT_NAME_CUSTOM_AREA_BOTTOM , OPTION_TITLE_SMQYXCmm} , {SANE_STD_OPT_NAME_SIZE_CHECK , OPTION_TITLE_CCJC} , {SANE_STD_OPT_NAME_PAGE , OPTION_TITLE_SMYM} , {SANE_STD_OPT_NAME_DISCARD_BLANK_SENS , OPTION_TITLE_TGKBYLMD} , {SANE_STD_OPT_NAME_RESOLUTION , OPTION_TITLE_FBL} , {SANE_STD_OPT_NAME_TIME_TO_SLEEP , OPTION_TITLE_XMSJ} , {SANE_STD_OPT_NAME_IMAGE_QUALITY , OPTION_TITLE_HZ} , {SANE_STD_OPT_NAME_EXCHANGE ,OPTION_TITLE_JHZFM} , {SANE_STD_OPT_NAME_SPLIT ,OPTION_TITLE_TXCF } , {SANE_STD_OPT_NAME_ANTI_SKEW , OPTION_TITLE_ZDJP} , {SANE_STD_OPT_NAME_IS_CUSTOM_GAMMA , OPTION_TITLE_QYSDQX} , {SANE_STD_OPT_NAME_GAMMA , OPTION_TITLE_JMZ} , {SANE_STD_OPT_NAME_BRIGHTNESS , OPTION_TITLE_LDZ} , {SANE_STD_OPT_NAME_CONTRAST , OPTION_TITLE_DBD} , {SANE_STD_OPT_NAME_IS_PHOTO_MODE , OPTION_TITLE_ZPMS} , {SANE_STD_OPT_NAME_ERASE_BLACK_FRAME , OPTION_TITLE_XCHK} , {SANE_STD_OPT_NAME_DARK_SAMPLE , OPTION_TITLE_SSYZ} , {SANE_STD_OPT_NAME_THRESHOLD , OPTION_TITLE_YZ} , {SANE_STD_OPT_NAME_ANTI_NOISE_LEVEL , OPTION_TITLE_BJKZDJ} , {SANE_STD_OPT_NAME_MARGIN , OPTION_TITLE_BYSJ} , {SANE_STD_OPT_NAME_FILL_BKG_MODE , OPTION_TITLE_BJTCFS} , {SANE_STD_OPT_NAME_IS_ANTI_PERMEATE , OPTION_TITLE_FZST} , {SANE_STD_OPT_NAME_ANTI_PERMEATE_LEVEL , OPTION_TITLE_FZSTDJ} , {SANE_STD_OPT_NAME_RID_HOLE_L , OPTION_TITLE_CKYCZC} , {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_L , OPTION_TITLE_ZCCKSSFWZFMBL} , {SANE_STD_OPT_NAME_RID_HOLE_R , OPTION_TITLE_CKYCYC} , {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_R , OPTION_TITLE_YCCKSSFWZFMBL} , {SANE_STD_OPT_NAME_RID_HOLE_T , OPTION_TITLE_CKYCSC} , {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_T , OPTION_TITLE_SCCKSSFWZFMBL} , {SANE_STD_OPT_NAME_RID_HOLE_B , OPTION_TITLE_CKYCXC} , {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_B , OPTION_TITLE_XCCKSSFWZFMBL} , {SANE_STD_OPT_NAME_IS_FILL_COLOR , OPTION_TITLE_SCTC} , {SANE_STD_OPT_NAME_IS_ULTROSONIC_CHECK , OPTION_TITLE_CSBJC} , {SANE_STD_OPT_NAME_DOUBLE_FEED_HANDLE , OPTION_TITLE_SZTPCL} , {SANE_STD_OPT_NAME_IS_CHECK_STAPLE , OPTION_TITLE_ZDJC} , {SANE_STD_OPT_NAME_SCAN_MODE , OPTION_TITLE_SMZS} , {SANE_STD_OPT_NAME_SCAN_COUNT , OPTION_TITLE_SMSL} , {SANE_STD_OPT_NAME_TEXT_DIRECTION , OPTION_TITLE_WGFX} , {SANE_STD_OPT_NAME_IS_ROTATE_BKG_180 , OPTION_TITLE_BMXZ180} , {SANE_STD_OPT_NAME_IS_CHECK_DOG_EAR , OPTION_TITLE_ZJJC} , {SANE_STD_OPT_NAME_DOG_EAR_SIZE , OPTION_TITLE_ZJDX} , {SANE_STD_OPT_NAME_IS_CHECK_ASKEW , OPTION_TITLE_WXJC} , {SANE_STD_OPT_NAME_ASKEW_RANGE , OPTION_TITLE_WXRRD} , {SANE_STD_OPT_NAME_FEED_STRENGTH , OPTION_TITLE_FZQD} , {SANE_STD_OPT_NAME_IS_AUTO_FEED_STRENGTH , OPTION_TITLE_ZDFZQD} , {SANE_STD_OPT_NAME_FEED_STRENGTH_VALUE , OPTION_TITLE_JZSBL} , {SANE_STD_OPT_NAME_WAIT_TO_SCAN , OPTION_TITLE_DZSM} }, g_discard[] = { {SANE_STD_OPT_NAME_REVERSE_01 , "\351\273\221\347\231\275\345\233\276\345\203\217\345\217\215\350\211\262\350\276\223\345\207\272\357\274\210\346\255\243\345\270\270\351\242\234\350\211\262\344\270\272\357\274\2320-\351\273\221\350\211\262\357\274\2331-\347\231\275\350\211\262\357\274\211"} // 黑白图像反色输出(正常颜色为:0-黑色;1-白色) , {SANE_STD_OPT_NAME_FILTER , "\347\201\260\345\272\246\346\210\226\351\273\221\347\231\275\345\233\276\345\203\217 - \351\231\244\350\211\262"} // 灰度或黑白图像 - 除色 , {SANE_STD_OPT_NAME_IS_AUTO_FEED_STRENGTH , "\350\207\252\345\212\250\346\220\223\347\272\270\345\274\272\345\272\246"} // 自动搓纸强度 , {SANE_STD_OPT_NAME_FEED_STRENGTH_VALUE , "\346\220\223\347\272\270\351\230\210\345\200\274"} // " 搓纸阈值" }; config::config() : ini_(NULL), file_(""), schem_jsn_(NULL) { } config::~config() { if(ini_) delete ini_; if(schem_jsn_) delete schem_jsn_; } QString config::get_scanner_config_file(void) { QString file(get_val("scanner", "schemes")); if(file.length() == 0) { HGChar logpath[512] = {0}; HGBase_GetConfigPath(logpath, 512); file = strcat(logpath, "scanner.schm"); ini_->setValue("schemes", file); } if(file[0] != '/') { std::string path(file_.toStdString()); path.erase(path.rfind('/') + 1); path += file.toStdString(); file = QString::fromStdString(path); } return file; } void config::reload_schemes(void) { if(schem_jsn_) { delete schem_jsn_; schem_jsn_ = NULL; } std::string jsntxt(""), org(config::read_mini_file(get_scanner_config_file())); gb::base64 base64; if(org.empty()) return; jsntxt = base64.decode(org.c_str(), org.length()); schem_jsn_ = new gb::json(); if(!schem_jsn_->attach_text(&jsntxt[0])) { delete schem_jsn_; schem_jsn_ = NULL; } } QString config::self_path(void) { QString qexePath = QCoreApplication::applicationDirPath(); return qexePath; } std::string config::read_mini_file(QString file) { std::string f(file.toStdString()), ret(""); FILE* src = fopen(f.c_str(), "rb"); if(src) { long l = 0; char *buf = NULL; fseek(src, 0, SEEK_END); l = ftell(src); fseek(src, 0, SEEK_SET); buf = (char*)malloc(l + 4); memset(buf, 0, l + 4); fread(buf, 1, l, src); fclose(src); ret = buf; free(buf); } return ret; } std::string config::device_to_config_dev_name(QString& dev_name) { std::string name(dev_name.toStdString()); size_t pos = name.find(" - "); if(pos != std::string::npos) { pos = name.find(" - ", pos + 3); // the first is ' - PID' 2022-10-11 if(pos != std::string::npos) name.erase(pos); } return name; } int config::save_2_file(QString file, const void* buf, size_t l) { FILE* dst = fopen(file.toStdString().c_str(), "wb"); if(!dst) return errno; fwrite(buf, 1, l, dst); fclose(dst); return 0; } int config::find_config(QString dev_name, std::vector& cfgs) { std::string name(config::device_to_config_dev_name(dev_name)); int ind = -1; for(size_t i = 0; i < cfgs.size(); ++i) { if(cfgs[i].name == name) { ind = i; break; } } return ind; } bool config::is_accessible_file(const std::string& path_file) { size_t pos = path_file.rfind('.'); if(pos++ == std::string::npos) return false; std::string ext(path_file.substr(pos)); config::to_lower(ext); return (ext == "png" || ext == "jpg" || ext == "jpeg" || ext == "bmp" || ext == "tif" || ext == "pdf" || ext == "png"); } void config::to_lower(std::string& str) { for(size_t i = 0; i < str.length(); ++i) { if(str[i] >= 'A' && str[i] <= 'Z') str[i] -= 'A' - 'a'; } } bool config::load_custom_gamma(const char* file, SANE_Gamma* gamma) { bool ret = false; FILE* src = fopen(file, "rb"); if(src) { long l = 0; SANE_Gamma tmp = {0}; fseek(src, 0, SEEK_END); l = ftell(src); fseek(src, 0, SEEK_SET); if(l == sizeof(SANE_Gamma) && fread(&tmp, l, 1, src) == 1) { if( tmp.pt_count >= 0 && tmp.pt_count <= sizeof (tmp.keypoint) / sizeof(tmp.keypoint[0]) && tmp.pt_count_r >= 0 && tmp.pt_count_r <= sizeof (tmp.keypoint_r) / sizeof(tmp.keypoint_r[0]) && tmp.pt_count_g >= 0 && tmp.pt_count_g <= sizeof (tmp.keypoint_g) / sizeof(tmp.keypoint_g[0]) && tmp.pt_count_b >= 0 && tmp.pt_count_b <= sizeof (tmp.keypoint_b) / sizeof(tmp.keypoint_b[0])) { memcpy(gamma, &tmp, l); ret = true; } } fclose(src); } return ret; } int config::load(QString file) { if(ini_) delete ini_; ini_ = NULL; if(file.length() == 0) file = "scanner.conf"; file_ = file; if(file_[0] != '/') { HGChar logpath[512] = {0}; HGBase_GetConfigPath(logpath, sizeof(logpath) / sizeof(logpath[0]) - 1); file_ = logpath; file_ += file; } ini_ = new QSettings(file_, QSettings::IniFormat); ini_->setIniCodec(QTextCodec::codecForName("UTF-8")); reload_schemes(); return 0; } QString config::get_val(QString sec_name, QString key, QString def_val) { if(!ini_) return def_val; QVariant qv = ini_->value(sec_name + "/" + key); char buf[128]; if(qv.isNull()) return def_val; if(qv.type() == QVariant::Type::Int) { sprintf(buf, "%d", qv.toInt()); return QString::fromStdString(buf); } else if(qv.type() == QVariant::Type::UInt) { sprintf(buf, "%u", qv.toInt()); return QString::fromStdString(buf); } else if(qv.type() == QVariant::Type::Double) { sprintf(buf, "%f", qv.toFloat()); return QString::fromStdString(buf); } else return qv.toString(); } QString config::get_file(void) { return file_; } void config::load_all_scanner_configs(std::vector& cfgs) { #if 0 _opt_scheme opt1; opt1.name = "aaa"; _opt_scheme opt2; opt2.name = "bbb"; DEVCFG d1; d1.name = "d1"; d1.schemes.push_back(opt1); d1.schemes.push_back(opt2); cfgs.push_back(d1); _opt_scheme opt3; opt3.name = "ccc"; _opt_scheme opt4; opt4.name = "ddd"; DEVCFG d2; d2.name = "d2"; d2.schemes.push_back(opt1); d2.schemes.push_back(opt2); cfgs.push_back(d2); #endif if(!schem_jsn_) return; std::vector devs; std::string cont(""), name(""); if(schem_jsn_->first_child(cont, &name)) { do { if(!name.empty()) devs.push_back(name); }while(schem_jsn_->next_child(cont, &name)); } for(size_t i = 0; i < devs.size(); ++i) { DEVCFG cfg; load_scanner_configs(QString::fromStdString(devs[i]), &cfg); cfgs.push_back(cfg); } } void transfer_name_from_cn_title_2_en_name(std::string& name) { int size = sizeof(g_opts) / sizeof(g_opts[0]); for (size_t i = 0; i < size; i++) { if (strcmp(name.c_str(), g_opts[i].title) == 0) { name = g_opts[i].name; } } } std::string config::transfer_en_name_2_cn_title(std::string& name) { int size = sizeof(g_opts) / sizeof(g_opts[0]); for (size_t i = 0; i < size; i++) { if (strcmp(name.c_str(), g_opts[i].name) == 0) { name = g_opts[i].title; } } return name; } void config::load_scanner_configs(QString dev_name, DEVCFG* cfg) { std::string name(config::device_to_config_dev_name(dev_name)), cont(""); OPTSCHEME scheme; OPTVAL val; cfg->name = name; cfg->cur_scheme = -1; if(!schem_jsn_) return; gb::json *child = NULL; schem_jsn_->get_value(name.c_str(), child); if(!child) return; if(child->first_child(cont)) { gb::json *cur = new gb::json(); if(cur->attach_text(&cont[0])) { if(!cur->get_value("cur_sel", cfg->cur_scheme)) cfg->cur_scheme = -1; } delete cur; while(child->next_child(cont)) { if(cont.empty()) continue; gb::json jsn, *son = NULL; if(!jsn.attach_text(&cont[0])) continue; jsn.get_value("scheme", scheme.name); jsn.get_value("opts", son); if(!son) continue; scheme.opts.clear(); if(son->first_child(cont)) { do { if(cont.empty()) continue; gb::json item; if(!item.attach_text(&cont[0])) continue; if(item.get_value("name", val.name) && item.get_value("value", val.val)) { transfer_name_from_cn_title_2_en_name(val.name); // chinese title to english name item.get_value("extra", val.extra); scheme.opts.push_back(val); } }while(son->next_child(cont)); } delete son; if(scheme.opts.size()) cfg->schemes.push_back(scheme); } } delete child; } int config::save_scanner_configs(const DEVCFG* cfg) { if(!schem_jsn_) schem_jsn_ = new gb::json(); gb::json *child = NULL, *scheme = NULL, *val = NULL; std::string text(""); schem_jsn_->get_value(cfg->name.c_str(), child); if(child) schem_jsn_->remove(cfg->name.c_str()); if(child) child->clear(); else { child = new gb::json(); } child->create_empty(true); scheme = new gb::json(); scheme->set_value("cur_sel", cfg->cur_scheme); scheme->set_value("version", cfg->appVersionNum); child->set_value(NULL, scheme); delete scheme; for(size_t i = 0; i < cfg->schemes.size(); ++i) { if(cfg->schemes[i].opts.empty()) continue; scheme = new gb::json(); scheme->create_empty(); scheme->set_value("scheme", cfg->schemes[i].name); gb::json *opt = new gb::json(); opt->create_empty(true); for(size_t j = 0; j < cfg->schemes[i].opts.size(); ++j) { val = new gb::json(); val->set_value("name", cfg->schemes[i].opts[j].name); // val->set_value("type", cfg->schemes[i].opts[j].type); val->set_value("extra", cfg->schemes[i].opts[j].extra); val->set_value("value", cfg->schemes[i].opts[j].val); text = val->to_string(false); opt->set_value(NULL, val); text = opt->to_string(false); delete val; } printf("scheme %d: %s\n", i + 1, text.c_str()); scheme->set_value("opts", opt); text = scheme->to_string(false); printf("all: %s\n", text.c_str()); delete opt; child->set_value(NULL, scheme); text = child->to_string(false); delete scheme; } schem_jsn_->set_value(cfg->name.c_str(), child); delete child; text = schem_jsn_->to_string(false); // save as base64 gb::base64 base64; std::string ec(base64.encode(text.c_str(), text.length())); return save_2_file(get_scanner_config_file(), ec.c_str(), ec.length()); }