• 在Ubuntu上用sane api实现通用扫描功能


    最近由于工作需要,要写一套扫描相关的接口。

    在这里记录一下,实现还有有点复杂的。

    目录

    依赖

    主要功能

    初始化

    获取当前扫描仪列表

    打开扫描仪

     sane_open

    设置扫描选项

    sane_control_option

    扫描

    关闭设备

    结束使用

    参考资料


    依赖

    sudo apt install libsane-dev  sane-utils

    主要功能

    初始化

    我们在操作扫描仪之前需要初始化才能正常使用。

    初始化使用的是sane里的sane_init。

    1. void scanner_init()
    2. {
    3. printf("[%s] Start\n", __FUNCTION__);
    4. SANE_Int version_code = 0;
    5. sane_init(&version_code, auth_callback);
    6. printf("SANE version code: %d\n", version_code);
    7. }
    8. static void
    9. auth_callback(SANE_String_Const resource,
    10. SANE_Char *username, SANE_Char *password)
    11. {
    12. }

    初始化成功则version_code为SANE_STATUS_GOOD(0)。

    获取当前扫描仪列表

    核心是sane_get_devices函数

    先通过sane_get_devices获取扫描仪列表,然后申请一个二维数组,将扫描仪列表放入二维数组中返回。

    1. const char **scanner_get_available_list()
    2. {
    3. printf("[%s] Start\n", __FUNCTION__);
    4. SANE_Status status;
    5. SANE_Int num_devices = 0;
    6. const SANE_Device **device_list;
    7. //获取扫描仪列表
    8. status = sane_get_devices(&device_list, SANE_FALSE);
    9. if (status != SANE_STATUS_GOOD)
    10. {
    11. printf("Error getting device list: %s\n", sane_strstatus(status));
    12. return NULL;
    13. }
    14. // 获取当前设备数量
    15. while (device_list[num_devices])
    16. num_devices++;
    17. // 如果设备列表为空,返回
    18. if (num_devices == 0)
    19. {
    20. printf("No scanners found.\n");
    21. sane_exit();
    22. return NULL;
    23. }
    24. // 分配内存
    25. const char **scanner_list = malloc(sizeof(SANE_Device *) * (num_devices + 1));
    26. if (!scanner_list)
    27. {
    28. printf("Failed to allocate memory.\n");
    29. sane_exit();
    30. return NULL;
    31. }
    32. // 继续分配内存
    33. for (int i = 0; i < num_devices; i++)
    34. {
    35. scanner_list[i] = strdup(device_list[i]->name);
    36. if (!scanner_list[i])
    37. {
    38. printf("Failed to allocate memory.\n");
    39. for (int j = 0; j < i; j++)
    40. {
    41. free(scanner_list[j]);
    42. }
    43. free(scanner_list);
    44. sane_exit();
    45. return NULL;
    46. }
    47. }
    48. scanner_list[num_devices] = NULL;
    49. // 返回设备列表
    50. return scanner_list;
    51. }

    我们再把获取到的设备列表循环打印一下。

    1. const char **scanner_list = scanner_get_available_list();
    2. if (scanner_list != NULL)
    3. {
    4. for (int num_devices = 0; scanner_list[num_devices]; ++num_devices)
    5. {
    6. if (scanner_list[num_devices] != NULL) // 添加一个检查
    7. {
    8. printf("Device %d: name=%s \n",
    9. num_devices, scanner_list[num_devices]);
    10. }
    11. }
    12. }
    13. else
    14. {
    15. return;
    16. }

    打开扫描仪

     sane_open

    这里介绍一下sane_open,第一个参数是扫描仪的名称,第二个参数是一个空的句柄,打开后通过句柄进行后续操作。

    1. extern SANE_Status sane_open (SANE_String_Const devicename,
    2. SANE_Handle * handle);

     函数的具体实现如下:

    1. static SANE_Handle sane_handle = NULL; // 扫描仪设备句柄,全局变量
    2. int scanner_open_device(char *scanner_name)
    3. {
    4. printf("[%s] Start\n", __FUNCTION__);
    5. SANE_Status sane_status = 0;
    6. if (sane_status = sane_open(scanner_name, &sane_handle))
    7. {
    8. printf("sane_open status: %s\n", sane_strstatus(sane_status));
    9. }
    10. if (sane_status != SANE_STATUS_GOOD)
    11. sane_handle = NULL;
    12. return sane_status;
    13. }

    这里的入参scanner_name是扫描仪列表的scanner_list,如果要打开第一个扫描仪的话是scanner_list[0]。

    如果函数的返回值不是SANE_STATUS_GOOD,表示打开失败了。


    设置扫描选项

    sane_control_option

    扫描的所有参数都是通过sane_control_option实现的,每个参数的功能详见备注。

    1. extern SANE_Status sane_control_option (SANE_Handle handle, //sane_open的句柄
    2. SANE_Int option, //要设置选项的序号,2是颜色,3是分辨率
    3. SANE_Action action, //操作的类型,给选项赋值或者获取当前值
    4. void *value, //value的实际值
    5. SANE_Int *info); //没发现有什么用

     操作的类型一共有以下三种,我们这里只用到第二种。

    1. typedef enum
    2. {
    3. SANE_ACTION_GET_VALUE = 0,
    4. SANE_ACTION_SET_VALUE,
    5. SANE_ACTION_SET_AUTO
    6. }
    7. SANE_Action;

    我这里设置了颜色,扫描的分辨率和纸张大小,还有很多可以设置的选项,可以自行探索。

    1. static SANE_Handle sane_handle = NULL; // 扫描仪设备句柄,全局变量
    2. // 设置指定扫描仪颜色,通过传入参数val_color进行设置扫描设备的颜色
    3. int scanner_set_color(SANE_String val_color)
    4. {
    5. printf("[%s] Start!\n", __FUNCTION__);
    6. SANE_Status status;
    7. status = sane_control_option(sane_handle, 2,
    8. SANE_ACTION_SET_VALUE, val_color, NULL);
    9. if (status != SANE_STATUS_GOOD)
    10. {
    11. printf("Option did not set, desc = %s\n", sane_strstatus(status));
    12. return status;
    13. }
    14. printf("set color option success!\n");
    15. return status;
    16. }
    17. // 设置指定扫描仪扫描的分辨率(清晰程度,分辨率越大越清晰)
    18. int scanner_set_resolutions(SANE_Int val_resolution)
    19. {
    20. printf("[%s] Start!\n", __FUNCTION__);
    21. SANE_Status status;
    22. status = sane_control_option(sane_handle, 3, SANE_ACTION_SET_VALUE, &val_resolution, NULL);
    23. if (status != SANE_STATUS_GOOD)
    24. {
    25. printf("Option did not set, desc = %s\n", sane_strstatus(status));
    26. return status;
    27. }
    28. printf("set resolution option success!\n");
    29. return status;
    30. }

    设置纸张大小有点复杂,因为纸张大小没有对应的option。因此曲线救国,通过设置扫描的纸张的长和宽来实现。长和宽的option序号分别是9和10。

    1. enum sizes_type
    2. {
    3. A2 = 1,
    4. A3,
    5. A4,
    6. A5,
    7. A6
    8. };
    9. static double g_saneSizeA4BrY = 297;
    10. int scanner_set_size(SANE_String size)
    11. {
    12. printf("[%s] Start!\n", __FUNCTION__);
    13. SANE_Status status = SANE_STATUS_GOOD;
    14. int type;
    15. if (!strcmp("A2", size))
    16. {
    17. type = A2;
    18. }
    19. else if (!strcmp("A3", size))
    20. {
    21. type = A3;
    22. }
    23. else if (!strcmp("A4", size))
    24. {
    25. type = A4;
    26. }
    27. else if (!strcmp("A5", size))
    28. {
    29. type = A5;
    30. }
    31. else if (!strcmp("A6", size))
    32. {
    33. type = A6;
    34. }
    35. else
    36. {
    37. type = 0;
    38. }
    39. switch (type)
    40. {
    41. case A2:
    42. status = kdk_scanner_set_size_real(sane_handle, 420, 594);
    43. break;
    44. case A3:
    45. status = kdk_scanner_set_size_real(sane_handle, 297, 420);
    46. break;
    47. case A4:
    48. status = kdk_scanner_set_size_real(sane_handle, 210, g_saneSizeA4BrY);
    49. break;
    50. case A5:
    51. status = kdk_scanner_set_size_real(sane_handle, 148, 210);
    52. break;
    53. case A6:
    54. status = kdk_scanner_set_size_real(sane_handle, 105, 144);
    55. break;
    56. default:
    57. status = SANE_STATUS_UNSUPPORTED;
    58. }
    59. return status;
    60. }
    61. /**
    62. * @brief scanner_set_size_real 统一设置扫描设备尺寸
    63. *
    64. * @param sane_handle 扫描句柄
    65. *
    66. * @param val_size_br_x 扫描设备右下角的x坐标
    67. *
    68. * @param val_size_br_y 扫描设备右下角的y坐标
    69. *
    70. * @return 返回扫描设备设置尺寸的情况
    71. */
    72. SANE_Status scanner_set_size_real(SANE_Handle sane_handle, SANE_Int val_size_br_x,
    73. SANE_Int val_size_br_y)
    74. {
    75. printf("[%s] Start!\n", __FUNCTION__);
    76. SANE_Status status = SANE_STATUS_GOOD;
    77. SANE_Word x = SANE_FIX(val_size_br_x);
    78. SANE_Word y = SANE_FIX(val_size_br_y);
    79. SANE_Word zero = SANE_FIX(0.0);
    80. status = sane_control_option(sane_handle, 7, SANE_ACTION_SET_VALUE, &zero, NULL);
    81. if (status != SANE_STATUS_GOOD)
    82. {
    83. return status;
    84. }
    85. status = sane_control_option(sane_handle, 8, SANE_ACTION_SET_VALUE, &zero, NULL);
    86. if (status != SANE_STATUS_GOOD)
    87. {
    88. return status;
    89. }
    90. status = sane_control_option(sane_handle, 9,
    91. SANE_ACTION_SET_VALUE, &x, NULL);
    92. if (status != SANE_STATUS_GOOD)
    93. {
    94. printf("Option x did not set, desc = %s\n", sane_strstatus(status));
    95. return status;
    96. }
    97. status = sane_control_option(sane_handle, 10,
    98. SANE_ACTION_SET_VALUE, &y, NULL);
    99. if (status != SANE_STATUS_GOOD)
    100. {
    101. printf("Option y did not set, desc = %s\n", sane_strstatus(status));
    102. return status;
    103. }
    104. return status;
    105. }

    和前面一样,如果函数的返回值不是SANE_STATUS_GOOD,表示失败了。

    扫描

    扫描我分成两类,分为单页单面扫描和多页双面扫描。

    单页和多页也是一种可以设置的扫描属性,单页和多页的主要是这个这个属性的区别,别的部分都差不多。

    1. //设置扫描是单页还是多页
    2. int scanner_set_page_type(SANE_Int type)
    3. {
    4. printf("[%s] Start!\n", __FUNCTION__);
    5. SANE_Status status;
    6. //对应的option序号为4
    7. status = sane_control_option(sane_handle, 4,
    8. SANE_ACTION_SET_VALUE, &type, NULL);
    9. if (status != SANE_STATUS_GOOD)
    10. {
    11. printf("Option did not set, desc = %s\n", sane_strstatus(status));
    12. return status;
    13. }
    14. printf("set page type option success!\n");
    15. return status;
    16. }

    单页单面扫描,就是不管有多少页都只扫描第一页的第一面。

    1. /**
    2. * @brief 指定扫描仪进行扫描(统一按照多页,双面处理)
    3. *
    4. * @param fileName:保存扫描文件的文件名,比如传test的话,扫描后的文件会是test_1,test_2之类的形式
    5. *
    6. * @param type:扫描类型 0:单面单面扫描,1:多页双面扫描
    7. *
    8. * @return 操作的返回值,0或者7为成功,其他为失败
    9. */
    10. int scanner_start_scan(SANE_String_Const fileName, int type)
    11. {
    12. printf("[%s] Start\n", __FUNCTION__);
    13. SANE_Status sane_status = 0;
    14. switch (type)
    15. {
    16. case 0:
    17. return do_scan_one(fileName);
    18. case 1:
    19. return do_scan_all(fileName);
    20. default:
    21. return do_scan_all(fileName);
    22. }
    23. }

    这是扫描单页的接口

    1. // 单页扫描
    2. SANE_Status do_scan_one(const char *fileName)
    3. {
    4. printf("[%s] Start\n", __FUNCTION__);
    5. del_old_pic();//扫描之前删掉上一次的内容
    6. SANE_Status status;
    7. FILE *ofp = NULL;
    8. char path[PATH_MAX];
    9. char part_path[PATH_MAX];
    10. buffer_size = (32 * 1024);
    11. buffer = (SANE_Byte *)malloc(buffer_size);
    12. int i = 1;
    13. // 设置打印机单页进纸张
    14. status = kdk_scanner_set_page_type(1);
    15. if (status != SANE_STATUS_GOOD)
    16. {
    17. printf("set page type fail:%s\n", sane_strstatus(status));
    18. return status;
    19. }
    20. do
    21. {
    22. // 设置保存路径
    23. sprintf(path, "/tmp/%s-%d.pnm", fileName, i); // 格式化PNM文件路径
    24. strcpy(part_path, path);
    25. strcat(part_path, ".part");
    26. printf("picture name: %s\n", path);
    27. // 开始扫描
    28. status = sane_start(sane_handle);
    29. if (status != SANE_STATUS_GOOD)
    30. {
    31. break;
    32. }
    33. if (NULL == (ofp = fopen(part_path, "w")))
    34. {
    35. status = SANE_STATUS_ACCESS_DENIED;
    36. break;
    37. }
    38. // 保存扫描数据
    39. status = scan_it(ofp);
    40. switch (status)
    41. {
    42. case SANE_STATUS_GOOD:
    43. case SANE_STATUS_EOF:
    44. {
    45. status = SANE_STATUS_GOOD;
    46. if (!ofp || 0 != fclose(ofp))
    47. {
    48. status = SANE_STATUS_ACCESS_DENIED;
    49. break;
    50. }
    51. else
    52. {
    53. ofp = NULL;
    54. if (rename(part_path, path))
    55. {
    56. status = SANE_STATUS_ACCESS_DENIED;
    57. break;
    58. }
    59. }
    60. }
    61. break;
    62. default:
    63. break;
    64. }
    65. } while (0);
    66. if (ofp)
    67. {
    68. fclose(ofp);
    69. ofp = NULL;
    70. }
    71. if (buffer)
    72. {
    73. free(buffer);
    74. buffer = NULL;
    75. }
    76. return status;
    77. }
    78. // 删除上一次扫描的文件
    79. void del_old_pic()
    80. {
    81. DIR *dir;
    82. struct dirent *entry;
    83. char path[] = "/tmp/";
    84. char ext[] = ".pnm";
    85. dir = opendir(path);
    86. if (dir == NULL)
    87. {
    88. perror("Unable to open directory");
    89. exit(EXIT_FAILURE);
    90. }
    91. while ((entry = readdir(dir)) != NULL)
    92. {
    93. // Check if the entry is a file and ends with .pnm
    94. if (entry->d_type == DT_REG &&
    95. strstr(entry->d_name, ext) != NULL &&
    96. strcmp(entry->d_name + strlen(entry->d_name) - strlen(ext), ext) == 0)
    97. {
    98. char full_path[512];
    99. sprintf(full_path, "%s%s", path, entry->d_name);
    100. if (remove(full_path) == 0)
    101. {
    102. printf("Deleted %s\n", full_path);
    103. }
    104. else
    105. {
    106. perror("Unable to delete file");
    107. }
    108. }
    109. }
    110. closedir(dir);
    111. }
    112. // sane 设置扫描方式
    113. int kdk_scanner_set_page_type(SANE_Int type)
    114. {
    115. printf("[%s] Start!\n", __FUNCTION__);
    116. SANE_Status status;
    117. status = sane_control_option(sane_handle, 4,
    118. SANE_ACTION_SET_VALUE, &type, NULL);
    119. if (status != SANE_STATUS_GOOD)
    120. {
    121. printf("Option did not set, desc = %s\n", sane_strstatus(status));
    122. return status;
    123. }
    124. printf("set page type option success!\n");
    125. return status;
    126. }

    保存图片,这一部分细节很多,我也没仔细研究,直接用就行。

    1. static SANE_Status scan_it(FILE *ofp)
    2. {
    3. int i, len, first_frame = 1, offset = 0, must_buffer = 0, hundred_percent;
    4. SANE_Byte min = 0xff, max = 0;
    5. SANE_Parameters parm;
    6. SANE_Status status;
    7. Image image = {0, 0, 0, 0, 0};
    8. static const char *format_name[] = {"gray", "RGB", "red", "green", "blue"};
    9. SANE_Word total_bytes = 0, expected_bytes;
    10. SANE_Int hang_over = -1;
    11. do
    12. {
    13. if (!first_frame)
    14. {
    15. status = sane_start(sane_handle);
    16. if (status != SANE_STATUS_GOOD)
    17. {
    18. goto cleanup;
    19. }
    20. }
    21. status = sane_get_parameters(sane_handle, &parm);
    22. if (status != SANE_STATUS_GOOD)
    23. {
    24. goto cleanup;
    25. }
    26. if (first_frame)
    27. {
    28. switch (parm.format)
    29. {
    30. case SANE_FRAME_RED:
    31. case SANE_FRAME_GREEN:
    32. case SANE_FRAME_BLUE:
    33. assert(parm.depth == 8);
    34. must_buffer = 1;
    35. offset = parm.format - SANE_FRAME_RED;
    36. break;
    37. case SANE_FRAME_RGB:
    38. assert((parm.depth == 8) || (parm.depth == 16));
    39. case SANE_FRAME_GRAY:
    40. assert((parm.depth == 1) || (parm.depth == 8) || (parm.depth == 16));
    41. if (parm.lines < 0)
    42. {
    43. must_buffer = 1;
    44. offset = 0;
    45. }
    46. else
    47. {
    48. write_pnm_header(parm.format, parm.pixels_per_line, parm.lines, parm.depth, ofp);
    49. }
    50. break;
    51. default:
    52. break;
    53. }
    54. if (must_buffer)
    55. {
    56. image.width = parm.bytes_per_line;
    57. if (parm.lines >= 0)
    58. image.height = parm.lines - STRIP_HEIGHT + 1;
    59. else
    60. image.height = 0;
    61. image.x = image.width - 1;
    62. image.y = -1;
    63. if (!advance(&image))
    64. {
    65. status = SANE_STATUS_NO_MEM;
    66. goto cleanup;
    67. }
    68. }
    69. }
    70. else
    71. {
    72. assert(parm.format >= SANE_FRAME_RED && parm.format <= SANE_FRAME_BLUE);
    73. offset = parm.format - SANE_FRAME_RED;
    74. image.x = image.y = 0;
    75. }
    76. hundred_percent = parm.bytes_per_line * parm.lines * ((parm.format == SANE_FRAME_RGB || parm.format == SANE_FRAME_GRAY) ? 1 : 3);
    77. // 这段是写图片数据
    78. while (1)
    79. {
    80. double progr;
    81. status = sane_read(sane_handle, buffer, buffer_size, &len);
    82. total_bytes += (SANE_Word)len;
    83. progr = ((total_bytes * 100.) / (double)hundred_percent);
    84. if (progr > 100.)
    85. progr = 100.;
    86. if (status != SANE_STATUS_GOOD)
    87. {
    88. if (status != SANE_STATUS_EOF)
    89. {
    90. return status;
    91. }
    92. break;
    93. }
    94. if (must_buffer)
    95. {
    96. switch (parm.format)
    97. {
    98. case SANE_FRAME_RED:
    99. case SANE_FRAME_GREEN:
    100. case SANE_FRAME_BLUE:
    101. for (i = 0; i < len; ++i)
    102. {
    103. image.data[offset + 3 * i] = buffer[i];
    104. if (!advance(&image))
    105. {
    106. status = SANE_STATUS_NO_MEM;
    107. goto cleanup;
    108. }
    109. }
    110. offset += 3 * len;
    111. break;
    112. case SANE_FRAME_RGB:
    113. for (i = 0; i < len; ++i)
    114. {
    115. image.data[offset + i] = buffer[i];
    116. if (!advance(&image))
    117. {
    118. status = SANE_STATUS_NO_MEM;
    119. goto cleanup;
    120. }
    121. }
    122. offset += len;
    123. break;
    124. case SANE_FRAME_GRAY:
    125. for (i = 0; i < len; ++i)
    126. {
    127. image.data[offset + i] = buffer[i];
    128. if (!advance(&image))
    129. {
    130. status = SANE_STATUS_NO_MEM;
    131. goto cleanup;
    132. }
    133. }
    134. offset += len;
    135. break;
    136. default:
    137. break;
    138. }
    139. }
    140. else /* ! must_buffer */
    141. {
    142. if ((parm.depth != 16))
    143. fwrite(buffer, 1, len, ofp);
    144. else
    145. {
    146. #if !defined(WORDS_BIGENDIAN)
    147. int i, start = 0;
    148. /* check if we have saved one byte from the last sane_read */
    149. if (hang_over > -1)
    150. {
    151. if (len > 0)
    152. {
    153. fwrite(buffer, 1, 1, ofp);
    154. buffer[0] = (SANE_Byte)hang_over;
    155. hang_over = -1;
    156. start = 1;
    157. }
    158. }
    159. /* now do the byte-swapping */
    160. for (i = start; i < (len - 1); i += 2)
    161. {
    162. unsigned char LSB;
    163. LSB = buffer[i];
    164. buffer[i] = buffer[i + 1];
    165. buffer[i + 1] = LSB;
    166. }
    167. /* check if we have an odd number of bytes */
    168. if (((len - start) % 2) != 0)
    169. {
    170. hang_over = buffer[len - 1];
    171. len--;
    172. }
    173. #endif
    174. fwrite(buffer, 1, len, ofp);
    175. }
    176. }
    177. if (verbose && parm.depth == 8)
    178. {
    179. for (i = 0; i < len; ++i)
    180. if (buffer[i] >= max)
    181. max = buffer[i];
    182. else if (buffer[i] < min)
    183. min = buffer[i];
    184. }
    185. }
    186. first_frame = 0;
    187. } while (!parm.last_frame);
    188. if (must_buffer)
    189. {
    190. image.height = image.y;
    191. write_pnm_header(parm.format, parm.pixels_per_line, image.height, parm.depth, ofp);
    192. #if !defined(WORDS_BIGENDIAN)
    193. if (parm.depth == 16)
    194. {
    195. int i;
    196. for (i = 0; i < image.height * image.width; i += 2)
    197. {
    198. unsigned char LSB;
    199. LSB = image.data[i];
    200. image.data[i] = image.data[i + 1];
    201. image.data[i + 1] = LSB;
    202. }
    203. }
    204. #endif
    205. fwrite(image.data, 1, image.height * image.width, ofp);
    206. }
    207. fflush(ofp);
    208. cleanup:
    209. if (image.data)
    210. free(image.data);
    211. return status;
    212. }
    213. void write_pnm_header(SANE_Frame format, int width, int height, int depth, FILE *ofp)
    214. {
    215. printf("[%s] Start\n", __FUNCTION__);
    216. switch (format)
    217. {
    218. case SANE_FRAME_RED:
    219. case SANE_FRAME_GREEN:
    220. case SANE_FRAME_BLUE:
    221. case SANE_FRAME_RGB:
    222. fprintf(ofp, "P6\n# SANE data follows\n%d %d\n%d\n", width, height, (depth <= 8) ? 255 : 65535);
    223. break;
    224. default:
    225. if (depth == 1)
    226. fprintf(ofp, "P4\n# SANE data follows\n%d %d\n", width, height);
    227. else
    228. fprintf(ofp, "P5\n# SANE data follows\n%d %d\n%d\n", width, height, (depth <= 8) ? 255 : 65535);
    229. break;
    230. }
    231. }
    232. static void *
    233. advance(Image *image)
    234. {
    235. if (++image->x >= image->width)
    236. {
    237. image->x = 0;
    238. if (++image->y >= image->height || !image->data)
    239. {
    240. size_t old_size = 0, new_size;
    241. if (image->data)
    242. old_size = image->height * image->width;
    243. image->height += STRIP_HEIGHT;
    244. new_size = image->height * image->width;
    245. if (image->data)
    246. image->data = realloc(image->data, new_size);
    247. else
    248. image->data = malloc(new_size);
    249. if (image->data)
    250. memset(image->data + old_size, 0, new_size - old_size);
    251. }
    252. }
    253. if (!image->data)
    254. fprintf(stderr, "can't allocate image buffer (%dx%d)\n",
    255. image->width, image->height);
    256. return image->data;
    257. }

    双页扫描,用do_scan_all替换do_scan_one,其他的函数都一样。

    1. // 双面扫描全部文件+保存为PNM图像格式
    2. SANE_Status do_scan_all(const char *fileName)
    3. {
    4. printf("[%s] Start\n", __FUNCTION__);
    5. SANE_Status status; // 返回状态
    6. FILE *ofp = NULL; // 输出文件指针
    7. char path[PATH_MAX]; // PNM文件路径
    8. char part_path[PATH_MAX]; // 临时PNN文件路径
    9. buffer_size = (32 * 1024); // 缓冲区大小
    10. buffer = malloc(buffer_size); // 动态分配缓冲区
    11. int i = 1;
    12. del_old_pic();
    13. //设置打印机多页进纸张
    14. status = kdk_scanner_set_page_type(0);
    15. if (status != SANE_STATUS_GOOD)
    16. {
    17. printf("set page type fail:%s\n", sane_strstatus(status));
    18. return status;
    19. }
    20. do
    21. {
    22. sprintf(path, "/tmp/%s-%d.pnm", fileName, i); // 格式化PNM文件路径
    23. strcpy(part_path, path); // 复制PNM文件路径到临时文件路径
    24. strcat(part_path, ".part"); // 在临时文件路径后添加扩展名".part"
    25. // 启动扫描过程
    26. status = sane_start(sane_handle);
    27. if (status != SANE_STATUS_GOOD)
    28. {
    29. break;
    30. }
    31. // 创建临时文件
    32. if (NULL == (ofp = fopen(part_path, "w")))
    33. {
    34. status = SANE_STATUS_ACCESS_DENIED;
    35. break;
    36. }
    37. // 进行扫描,并将结果写入到临时文件中
    38. status = scan_it(ofp);
    39. switch (status)
    40. {
    41. case SANE_STATUS_GOOD:
    42. case SANE_STATUS_EOF:
    43. {
    44. // 扫描成功或结束
    45. status = SANE_STATUS_GOOD;
    46. // 关闭临时文件,并检查是否成功关闭
    47. if (!ofp || 0 != fclose(ofp))
    48. {
    49. status = SANE_STATUS_ACCESS_DENIED;
    50. break;
    51. }
    52. else
    53. {
    54. ofp = NULL; // 将文件指针设置为NULL,避免重复关闭
    55. // 将临时文件重命名为正式的PNM文件
    56. if (rename(part_path, path))
    57. {
    58. status = SANE_STATUS_ACCESS_DENIED;
    59. break;
    60. }
    61. }
    62. }
    63. break;
    64. default:
    65. break;
    66. }
    67. i++;
    68. } while (status == SANE_STATUS_GOOD);
    69. // 如果出现错误,则取消扫描进程
    70. if (SANE_STATUS_GOOD != status)
    71. {
    72. sane_cancel(sane_handle);
    73. }
    74. // 关闭输出文件
    75. if (ofp)
    76. {
    77. fclose(ofp);
    78. ofp = NULL;
    79. }
    80. // 释放缓冲区内存
    81. if (buffer)
    82. {
    83. free(buffer);
    84. buffer = NULL;
    85. }
    86. if ((status == SANE_STATUS_NO_DOCS) && (i > 1))
    87. status = SANE_STATUS_GOOD;
    88. return status; // 返回状态
    89. }

    扫描完成会会在tmp下生成扫描文件。

    关闭设备

    1. void scanner_close_device()
    2. {
    3. printf("[%s] Start\n", __FUNCTION__);
    4. if (sane_handle != NULL)
    5. {
    6. sane_close(sane_handle);
    7. }
    8. sane_handle = NULL;
    9. }

    结束使用

    1. void scanner_exit()
    2. {
    3. printf("[%s] Start\n", __FUNCTION__);
    4. sane_exit();
    5. }

    参考资料

    Linux下通用扫描仪API——SANE( Scanner Access Now Easy)_linux sane-CSDN博客

  • 相关阅读:
    前端监控日志产品
    Go微服务框架go-kratos实战05:分布式链路追踪 OpenTelemetry 使用
    Angular8升级到Angular11
    大模型从入门到应用——LangChain:代理(Agents)-[工具包(Toolkit)]
    如何在Ubuntu系统部署RabbitMQ服务器并公网访问【内网穿透】
    Python学习笔记之进程池pool
    Linux 关闭对应端口号进程
    算法---二进制字符串重新安排顺序需要的时间(Kotlin)
    linux screen会话管理 断开连接恢复会话
    Pr:湍流置换
  • 原文地址:https://blog.csdn.net/weixin_48885322/article/details/134463289