Advertisement
Guest User

Untitled

a guest
Dec 9th, 2016
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.17 KB | None | 0 0
  1. // = Requirements: freetype 2.5, libpng, libicu, libz, libzip2
  2. // = How to compile:
  3. // % export CXXFLAGS=`pkg-config --cflags freetype2 libpng`
  4. // % export LDFLAGS=`pkg-config --libs freetype2 libpng`
  5. // % clang++ -o clfontpng -static $(CXXFLAGS) clfontpng.cc $(LDFLAGS) \
  6. // -licuuc -lz -lbz2
  7. #include <cassert>
  8. #include <cctype>
  9. #include <iostream>
  10. #include <memory>
  11. #include <vector>
  12. #include <string>
  13.  
  14. #include <stdio.h>
  15.  
  16. #include <unicode/umachine.h>
  17. #include <unicode/utf.h>
  18.  
  19. #include <ft2build.h>
  20. #include FT_FREETYPE_H
  21. #include FT_TRUETYPE_TABLES_H
  22.  
  23. #define PNG_SKIP_SETJMP_CHECK
  24. #include <png.h>
  25.  
  26. namespace {
  27.  
  28. const char* kDefaultOutputFile = "out.png";
  29. const int kBytesPerPixel = 4; // RGBA
  30. const int kDefaultPixelSize = 128;
  31. const int kSpaceWidth = kDefaultPixelSize / 2;
  32.  
  33. FT_Library gFtLibrary;
  34.  
  35. // Only support horizontal direction.
  36. class DrawContext {
  37. public:
  38. DrawContext()
  39. : pos_(0), width_(0), height_(0) {}
  40. uint8_t* Bitmap() { return &bitmap_[0]; }
  41. const uint32_t Width() const { return width_; }
  42. const uint32_t Height() const { return height_; }
  43. void SetSize(int width, int height) {
  44. width_ = width;
  45. height_ = height;
  46. int size = width * height * kBytesPerPixel;
  47. bitmap_.resize(size);
  48. bitmap_.assign(size, 0x00);
  49. }
  50. void Advance(int dx) { pos_ += dx; }
  51. uint8_t* GetDrawPosition(int row) {
  52. uint32_t index =(row * width_ + pos_) * kBytesPerPixel;
  53. assert(index < bitmap_.size());
  54. return &bitmap_[index];
  55. }
  56. private:
  57. DrawContext(const DrawContext&) = delete;
  58. DrawContext& operator=(const DrawContext&) = delete;
  59.  
  60. uint32_t pos_;
  61. uint32_t width_;
  62. uint32_t height_;
  63. std::vector<uint8_t> bitmap_;
  64. };
  65.  
  66. struct FaceOptions {
  67. int pixel_size;
  68. int load_flags;
  69. FT_Render_Mode render_mode;
  70. FaceOptions()
  71. : pixel_size(kDefaultPixelSize)
  72. , load_flags(0), render_mode(FT_RENDER_MODE_NORMAL) {}
  73. };
  74.  
  75. class FreeTypeFace {
  76. public:
  77. FreeTypeFace(const std::string& font_file)
  78. : font_file_(font_file)
  79. , options_()
  80. , face_(nullptr)
  81. {
  82. error_ = FT_New_Face(gFtLibrary, font_file_.c_str(), 0, &face_);
  83. if (error_) {
  84. face_ = nullptr;
  85. return;
  86. }
  87. if (IsColorEmojiFont())
  88. SetupColorFont();
  89. else
  90. SetupNormalFont();
  91. }
  92. ~FreeTypeFace() {
  93. if (face_)
  94. FT_Done_Face(face_);
  95. }
  96. FreeTypeFace(FreeTypeFace&& rhs)
  97. : font_file_(rhs.font_file_)
  98. , options_(rhs.options_)
  99. , face_(rhs.face_)
  100. , error_(rhs.error_)
  101. {
  102. rhs.face_ = nullptr;
  103. }
  104. bool CalculateBox(uint32_t codepoint, uint32_t& width, uint32_t& height) {
  105. if (!RenderGlyph(codepoint))
  106. return false;
  107. width += (face_->glyph->advance.x >> 6);
  108. height = std::max(
  109. height, static_cast<uint32_t>(face_->glyph->metrics.height >> 6));
  110. return true;
  111. }
  112. bool DrawCodepoint(DrawContext& context, uint32_t codepoint) {
  113. if (!RenderGlyph(codepoint))
  114. return false;
  115. printf("U+%08X -> %s\n", codepoint, font_file_.c_str());
  116. return DrawBitmap(context, face_->glyph);
  117. }
  118. int Error() const { return error_; }
  119.  
  120. private:
  121. FreeTypeFace(const FreeTypeFace&) = delete;
  122. FreeTypeFace& operator=(const FreeTypeFace&) = delete;
  123.  
  124. bool RenderGlyph(uint32_t codepoint) {
  125. if (!face_)
  126. return false;
  127. uint32_t glyph_index = FT_Get_Char_Index(face_, codepoint);
  128. if (glyph_index == 0)
  129. return false;
  130. error_ = FT_Load_Glyph(face_, glyph_index, options_.load_flags);
  131. if (error_)
  132. return false;
  133. error_ = FT_Render_Glyph(face_->glyph, options_.render_mode);
  134. if (error_)
  135. return false;
  136. return true;
  137. }
  138. bool IsColorEmojiFont() {
  139. static const uint32_t tag = FT_MAKE_TAG('C', 'B', 'D', 'T');
  140. unsigned long length = 0;
  141. FT_Load_Sfnt_Table(face_, tag, 0, nullptr, &length);
  142. if (length) {
  143. std::cout << font_file_ << " is color font" << std::endl;
  144. return true;
  145. }
  146. return false;
  147. }
  148. void SetupNormalFont() {
  149. error_ = FT_Set_Pixel_Sizes(face_, 0, options_.pixel_size);
  150. }
  151. void SetupColorFont() {
  152. options_.load_flags |= FT_LOAD_COLOR;
  153.  
  154. if (face_->num_fixed_sizes == 0)
  155. return;
  156. int best_match = 0;
  157. int diff = std::abs(options_.pixel_size - face_->available_sizes[0].width);
  158. for (int i = 1; i < face_->num_fixed_sizes; ++i) {
  159. int ndiff =
  160. std::abs(options_.pixel_size - face_->available_sizes[i].width);
  161. if (ndiff < diff) {
  162. best_match = i;
  163. diff = ndiff;
  164. }
  165. }
  166. error_ = FT_Select_Size(face_, best_match);
  167. }
  168. bool DrawBitmap(DrawContext& context, FT_GlyphSlot slot) {
  169. int pixel_mode = slot->bitmap.pixel_mode;
  170. if (pixel_mode == FT_PIXEL_MODE_BGRA)
  171. DrawColorBitmap(context, slot);
  172. else
  173. DrawNormalBitmap(context, slot);
  174. context.Advance(slot->advance.x >> 6);
  175. return true;
  176. }
  177. void DrawColorBitmap(DrawContext& context, FT_GlyphSlot slot) {
  178. uint8_t* src = slot->bitmap.buffer;
  179. // FIXME: Should use metrics for drawing. (e.g. calculate baseline)
  180. int yoffset = context.Height() - slot->bitmap.rows;
  181. for (int y = 0; y < slot->bitmap.rows; ++y) {
  182. uint8_t* dest = context.GetDrawPosition(y + yoffset);
  183. for (int x = 0; x < slot->bitmap.width; ++x) {
  184. uint8_t b = *src++, g = *src++, r = *src++, a = *src++;
  185. *dest++ = r; *dest++ = g; *dest++ = b; *dest++ = a;
  186. }
  187. }
  188. }
  189. void DrawNormalBitmap(DrawContext& context, FT_GlyphSlot slot) {
  190. uint8_t* src = slot->bitmap.buffer;
  191. // FIXME: Same as DrawColorBitmap()
  192. int yoffset = context.Height() - slot->bitmap.rows;
  193. for (int y = 0; y < slot->bitmap.rows; ++y) {
  194. uint8_t* dest = context.GetDrawPosition(y + yoffset);
  195. for (int x = 0; x < slot->bitmap.width; ++x) {
  196. *dest++ = 255 - *src;
  197. *dest++ = 255 - *src;
  198. *dest++ = 255 - *src;
  199. *dest++ = *src; // Alpha
  200. ++src;
  201. }
  202. }
  203. }
  204.  
  205. std::string font_file_;
  206. FaceOptions options_;
  207. FT_Face face_;
  208. int error_;
  209. };
  210.  
  211. class FontList {
  212. typedef std::vector<std::unique_ptr<FreeTypeFace>> FaceList;
  213. public:
  214. FontList() {}
  215.  
  216. void AddFont(const std::string& font_file) {
  217. auto face = std::unique_ptr<FreeTypeFace>(new FreeTypeFace(font_file));
  218. face_list_.push_back(std::move(face));
  219. }
  220. void CalculateBox(uint32_t codepoint, uint32_t& width, uint32_t& height) {
  221. static const uint32_t kSpace = 0x20;
  222. if (codepoint == kSpace) {
  223. width += kSpaceWidth;
  224. } else {
  225. for (auto& face : face_list_) {
  226. if (face->CalculateBox(codepoint, width, height))
  227. return;
  228. }
  229. }
  230. }
  231. void DrawCodepoint(DrawContext& context, uint32_t codepoint) {
  232. for (auto& face : face_list_) {
  233. if (face->DrawCodepoint(context, codepoint))
  234. return;
  235. }
  236. std::cerr << "Missing glyph for codepoint: " << codepoint << std::endl;
  237. }
  238.  
  239. private:
  240. FontList(const FontList&) = delete;
  241. FontList& operator=(const FontList&) = delete;
  242. FaceList face_list_;
  243. };
  244.  
  245. class PngWriter {
  246. public:
  247. PngWriter(const std::string& outfile)
  248. : outfile_(outfile), png_(nullptr), info_(nullptr)
  249. {
  250. fp_ = fopen(outfile_.c_str(), "wb");
  251. if (!fp_) {
  252. std::cerr << "Failed to open: " << outfile_ << std::endl;
  253. Cleanup();
  254. return;
  255. }
  256. png_ = png_create_write_struct(
  257. PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
  258. if (!png_) {
  259. std::cerr << "Failed to create PNG file" << std::endl;
  260. Cleanup();
  261. return;
  262. }
  263. info_ = png_create_info_struct(png_);
  264. if (!info_) {
  265. std::cerr << "Failed to create PNG file" << std::endl;
  266. Cleanup();
  267. return;
  268. }
  269. }
  270. ~PngWriter() { Cleanup(); }
  271. bool Write(uint8_t* rgba, int width, int height) {
  272. static const int kDepth = 8;
  273. if (!png_) {
  274. std::cerr << "Writer is not initialized" << std::endl;
  275. return false;
  276. }
  277. if (setjmp(png_jmpbuf(png_))) {
  278. std::cerr << "Failed to write PNG" << std::endl;
  279. Cleanup();
  280. return false;
  281. }
  282. png_set_IHDR(png_, info_, width, height, kDepth,
  283. PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
  284. PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  285. png_init_io(png_, fp_);
  286. png_byte** row_pointers =
  287. static_cast<png_byte**>(png_malloc(png_, height * sizeof(png_byte*)));
  288. uint8_t* src = rgba;
  289. for (int y = 0; y < height; ++y) {
  290. png_byte* row =
  291. static_cast<png_byte*>(png_malloc(png_, width * kBytesPerPixel));
  292. row_pointers[y] = row;
  293. for (int x = 0; x < width; ++x) {
  294. *row++ = *src++;
  295. *row++ = *src++;
  296. *row++ = *src++;
  297. *row++ = *src++;
  298. }
  299. assert(row - row_pointers[y] == width * kBytesPerPixel);
  300. }
  301. assert(src - rgba == width * height * kBytesPerPixel);
  302. png_set_rows(png_, info_, row_pointers);
  303. png_write_png(png_, info_, PNG_TRANSFORM_IDENTITY, 0);
  304. for (int y = 0; y < height; y++)
  305. png_free(png_, row_pointers[y]);
  306. png_free(png_, row_pointers);
  307. Cleanup();
  308. return true;
  309. }
  310. private:
  311. PngWriter(const PngWriter&) = delete;
  312. PngWriter operator=(const PngWriter&) = delete;
  313. void Cleanup() {
  314. if (fp_) { fclose(fp_); }
  315. if (png_) png_destroy_write_struct(&png_, &info_);
  316. fp_ = nullptr; png_ = nullptr; info_ = nullptr;
  317. }
  318.  
  319. std::string outfile_;
  320. FILE* fp_;
  321. png_structp png_;
  322. png_infop info_;
  323. char* rgba_;
  324. uint32_t width_;
  325. uint32_t height_;
  326. };
  327.  
  328. class App {
  329. public:
  330. void AddFont(const std::string& font_file) { font_list_.AddFont(font_file); }
  331. bool SetText(const char* text) { return UTF8ToCodepoint(text); }
  332. bool Execute() {
  333. CalculateImageSize();
  334. Draw();
  335. return Output();
  336. }
  337. private:
  338. bool UTF8ToCodepoint(const char* text) {
  339. int32_t i = 0, length = strlen(text), c;
  340. while (i < length) {
  341. U8_NEXT(text, i, length, c);
  342. if (c < 0) {
  343. std::cerr << "Invalid input text" << std::endl;
  344. return false;
  345. }
  346. codepoints_.push_back(c);
  347. }
  348. return true;
  349. }
  350. void CalculateImageSize() {
  351. uint32_t width = 0, height = 0;
  352. for (auto c : codepoints_)
  353. font_list_.CalculateBox(c, width, height);
  354. printf("width: %u, height: %u\n", width, height);
  355. draw_context_.SetSize(width, height);
  356. }
  357. void Draw() {
  358. for (auto c : codepoints_)
  359. font_list_.DrawCodepoint(draw_context_, c);
  360. }
  361. bool Output() {
  362. PngWriter writer(kDefaultOutputFile);
  363. return writer.Write(draw_context_.Bitmap(),
  364. draw_context_.Width(),
  365. draw_context_.Height());
  366. }
  367.  
  368. std::vector<uint32_t> codepoints_;
  369. FontList font_list_;
  370. DrawContext draw_context_;
  371. };
  372.  
  373. bool Init() {
  374. int error = FT_Init_FreeType(&gFtLibrary);
  375. if (error) {
  376. std::cerr << "Failed to initialize freetype" << std::endl;
  377. return error;
  378. }
  379. return error == 0;
  380. }
  381.  
  382. void Finish() {
  383. FT_Done_FreeType(gFtLibrary);
  384. }
  385.  
  386. void Usage() {
  387. std::cout
  388. << "Usage: clfontpng font1.ttf [font2.ttf ...] text"
  389. << std::endl;
  390. std::exit(1);
  391. }
  392.  
  393. bool ParseArgs(App& app, int argc, char** argv) {
  394. if (argc < 2)
  395. return false;
  396. for (int i = 1; i < argc - 1; ++i)
  397. app.AddFont(argv[i]);
  398. return app.SetText(argv[argc - 1]);
  399. }
  400.  
  401. bool Start(int argc, char** argv) {
  402. App app;
  403. if (!ParseArgs(app, argc, argv))
  404. Usage();
  405. return app.Execute();
  406. }
  407.  
  408. } // namespace
  409.  
  410. int main(int argc, char** argv) {
  411. if (!Init())
  412. std::exit(1);
  413. bool success = Start(argc, argv);
  414. Finish();
  415. return success ? 0 : 1;
  416. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement