text-options.hh 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /*
  2. * Copyright © 2011 Google, Inc.
  3. *
  4. * This is part of HarfBuzz, a text shaping library.
  5. *
  6. * Permission is hereby granted, without written agreement and without
  7. * license or royalty fees, to use, copy, modify, and distribute this
  8. * software and its documentation for any purpose, provided that the
  9. * above copyright notice and the following two paragraphs appear in
  10. * all copies of this software.
  11. *
  12. * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
  13. * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
  14. * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
  15. * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
  16. * DAMAGE.
  17. *
  18. * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  19. * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  20. * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
  21. * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  22. * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  23. *
  24. * Google Author(s): Behdad Esfahbod
  25. */
  26. #ifndef TEXT_OPTIONS_HH
  27. #define TEXT_OPTIONS_HH
  28. #include "options.hh"
  29. struct text_options_t
  30. {
  31. text_options_t ()
  32. : gs (g_string_new (nullptr))
  33. {}
  34. ~text_options_t ()
  35. {
  36. g_free (text);
  37. g_free (text_file);
  38. if (gs)
  39. g_string_free (gs, true);
  40. if (in_fp && in_fp != stdin)
  41. fclose (in_fp);
  42. }
  43. void add_options (option_parser_t *parser);
  44. void post_parse (GError **error G_GNUC_UNUSED)
  45. {
  46. if (!text && !text_file)
  47. text_file = g_strdup ("-");
  48. if (text && text_file)
  49. {
  50. g_set_error (error,
  51. G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
  52. "Only one of text and text-file can be set");
  53. return;
  54. }
  55. if (text_file)
  56. {
  57. if (0 != strcmp (text_file, "-"))
  58. in_fp = fopen (text_file, "r");
  59. else
  60. in_fp = stdin;
  61. if (!in_fp)
  62. g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
  63. "Failed opening text file `%s': %s",
  64. text_file, strerror (errno));
  65. }
  66. }
  67. const char *get_line (unsigned int *len);
  68. int text_len = -1;
  69. char *text = nullptr;
  70. char *text_file = nullptr;
  71. private:
  72. FILE *in_fp = nullptr;
  73. GString *gs = nullptr;
  74. char *line = nullptr;
  75. unsigned line_len = UINT_MAX;
  76. hb_bool_t single_par = false;
  77. };
  78. struct shape_text_options_t : text_options_t
  79. {
  80. ~shape_text_options_t ()
  81. {
  82. g_free (text_before);
  83. g_free (text_after);
  84. }
  85. void add_options (option_parser_t *parser);
  86. char *text_before = nullptr;
  87. char *text_after = nullptr;
  88. };
  89. static gboolean
  90. parse_text (const char *name G_GNUC_UNUSED,
  91. const char *arg,
  92. gpointer data,
  93. GError **error G_GNUC_UNUSED)
  94. {
  95. text_options_t *text_opts = (text_options_t *) data;
  96. if (text_opts->text)
  97. {
  98. g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
  99. "Either --text or --unicodes can be provided but not both");
  100. return false;
  101. }
  102. text_opts->text_len = -1;
  103. text_opts->text = g_strdup (arg);
  104. return true;
  105. }
  106. static bool
  107. encode_unicodes (const char *unicodes,
  108. GString *gs,
  109. GError **error)
  110. {
  111. #define DELIMITERS "<+-|>{},;&#\\xXuUnNiI\n\t\v\f\r "
  112. char *s = (char *) unicodes;
  113. char *p;
  114. while (s && *s)
  115. {
  116. while (*s && strchr (DELIMITERS, *s))
  117. s++;
  118. if (!*s)
  119. break;
  120. errno = 0;
  121. hb_codepoint_t u = strtoul (s, &p, 16);
  122. if (errno || s == p)
  123. {
  124. g_string_free (gs, TRUE);
  125. g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
  126. "Failed parsing Unicode value at: '%s'", s);
  127. return false;
  128. }
  129. g_string_append_unichar (gs, u);
  130. s = p;
  131. }
  132. #undef DELIMITERS
  133. return true;
  134. }
  135. static gboolean
  136. parse_unicodes (const char *name G_GNUC_UNUSED,
  137. const char *arg,
  138. gpointer data,
  139. GError **error G_GNUC_UNUSED)
  140. {
  141. text_options_t *text_opts = (text_options_t *) data;
  142. if (text_opts->text)
  143. {
  144. g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
  145. "Either --text or --unicodes can be provided but not both");
  146. return false;
  147. }
  148. GString *gs = g_string_new (nullptr);
  149. if (0 == strcmp (arg, "*"))
  150. g_string_append_c (gs, '*');
  151. else
  152. if (!encode_unicodes (arg, gs, error))
  153. return false;
  154. text_opts->text_len = gs->len;
  155. text_opts->text = g_string_free (gs, FALSE);
  156. return true;
  157. }
  158. static gboolean
  159. parse_text_before (const char *name G_GNUC_UNUSED,
  160. const char *arg,
  161. gpointer data,
  162. GError **error)
  163. {
  164. auto *opts = (shape_text_options_t *) data;
  165. if (opts->text_before)
  166. {
  167. g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
  168. "Either --text-before or --unicodes-before can be provided but not both");
  169. return false;
  170. }
  171. opts->text_before = g_strdup (arg);
  172. fprintf(stderr, "%s\n", opts->text_before);
  173. return true;
  174. }
  175. static gboolean
  176. parse_unicodes_before (const char *name G_GNUC_UNUSED,
  177. const char *arg,
  178. gpointer data,
  179. GError **error)
  180. {
  181. auto *opts = (shape_text_options_t *) data;
  182. if (opts->text_before)
  183. {
  184. g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
  185. "Either --text-before or --unicodes-before can be provided but not both");
  186. return false;
  187. }
  188. GString *gs = g_string_new (nullptr);
  189. if (!encode_unicodes (arg, gs, error))
  190. return false;
  191. opts->text_before = g_string_free (gs, FALSE);
  192. return true;
  193. }
  194. static gboolean
  195. parse_text_after (const char *name G_GNUC_UNUSED,
  196. const char *arg,
  197. gpointer data,
  198. GError **error)
  199. {
  200. auto *opts = (shape_text_options_t *) data;
  201. if (opts->text_after)
  202. {
  203. g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
  204. "Either --text-after or --unicodes-after can be provided but not both");
  205. return false;
  206. }
  207. opts->text_after = g_strdup (arg);
  208. return true;
  209. }
  210. static gboolean
  211. parse_unicodes_after (const char *name G_GNUC_UNUSED,
  212. const char *arg,
  213. gpointer data,
  214. GError **error)
  215. {
  216. auto *opts = (shape_text_options_t *) data;
  217. if (opts->text_after)
  218. {
  219. g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
  220. "Either --text-after or --unicodes-after can be provided but not both");
  221. return false;
  222. }
  223. GString *gs = g_string_new (nullptr);
  224. if (!encode_unicodes (arg, gs, error))
  225. return false;
  226. opts->text_after = g_string_free (gs, FALSE);
  227. return true;
  228. }
  229. const char *
  230. text_options_t::get_line (unsigned int *len)
  231. {
  232. if (text)
  233. {
  234. if (!line)
  235. {
  236. line = text;
  237. line_len = text_len;
  238. }
  239. if (line_len == UINT_MAX)
  240. line_len = strlen (line);
  241. if (!line_len)
  242. {
  243. *len = 0;
  244. return nullptr;
  245. }
  246. const char *ret = line;
  247. const char *p = single_par ? nullptr : (const char *) memchr (line, '\n', line_len);
  248. unsigned int ret_len;
  249. if (!p)
  250. {
  251. ret_len = line_len;
  252. line += ret_len;
  253. line_len = 0;
  254. }
  255. else
  256. {
  257. ret_len = p - ret;
  258. line += ret_len + 1;
  259. line_len -= ret_len + 1;
  260. }
  261. *len = ret_len;
  262. return ret;
  263. }
  264. g_string_set_size (gs, 0);
  265. char buf[BUFSIZ];
  266. while (fgets (buf, sizeof (buf), in_fp))
  267. {
  268. unsigned bytes = strlen (buf);
  269. if (!single_par && bytes && buf[bytes - 1] == '\n')
  270. {
  271. bytes--;
  272. g_string_append_len (gs, buf, bytes);
  273. break;
  274. }
  275. g_string_append_len (gs, buf, bytes);
  276. }
  277. if (ferror (in_fp))
  278. fail (false, "Failed reading text: %s", strerror (errno));
  279. *len = gs->len;
  280. return !*len && feof (in_fp) ? nullptr : gs->str;
  281. }
  282. void
  283. text_options_t::add_options (option_parser_t *parser)
  284. {
  285. GOptionEntry entries[] =
  286. {
  287. {"text", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Set input text", "string"},
  288. {"text-file", 0, 0, G_OPTION_ARG_STRING, &this->text_file, "Set input text file-name", "filename"},
  289. {"unicodes", 'u', 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Set input Unicode codepoints", "list of hex numbers"},
  290. {"single-par", 0, 0, G_OPTION_ARG_NONE, &this->single_par, "Treat text as single paragraph", nullptr},
  291. {nullptr}
  292. };
  293. parser->add_group (entries,
  294. "text",
  295. "Text options:\n\nIf no text is provided, standard input is used for input.\n",
  296. "Options for the input text",
  297. this);
  298. }
  299. void
  300. shape_text_options_t::add_options (option_parser_t *parser)
  301. {
  302. text_options_t::add_options (parser);
  303. GOptionEntry entries[] =
  304. {
  305. {"text-before", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text_before, "Set text context before each line", "string"},
  306. {"text-after", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text_after, "Set text context after each line", "string"},
  307. {"unicodes-before", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes_before, "Set Unicode codepoints context before each line", "list of hex numbers"},
  308. {"unicodes-after", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes_after, "Set Unicode codepoints context after each line", "list of hex numbers"},
  309. {nullptr}
  310. };
  311. parser->add_group (entries,
  312. "text-context",
  313. "Textual context options:",
  314. "Options for the input context text",
  315. this);
  316. }
  317. #endif