Skip to content

Commit f0cd898

Browse files
songhahaha66da-liii
authored andcommitted
!405 [206_2] 初步支持emoji绘制
1 parent 5b156b5 commit f0cd898

File tree

9 files changed

+169
-3
lines changed

9 files changed

+169
-3
lines changed

TeXmacs/fonts/font-characteristics.scm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,6 +1800,7 @@
18001800
((Norasi Regular) (Ascii Latin mono=no sans=no slant=0 italic=no case=mixed regular=yes ex=75 em=197 lvw=20 lhw=5 uvw=25 uhw=6 fillp=33 vcnt=34 lasprat=101 pasprat=94 loasc=152 lodes=148 dides=104))
18011801
((Noteworthy Bold) (Ascii Latin Greek Cyrillic mono=no sans=yes slant=0 italic=no case=mixed regular=no ex=89 em=164 lvw=21 lhw=17 uvw=21 uhw=17 fillp=46 vcnt=53 lasprat=88 pasprat=78 loasc=167 lodes=166 dides=108))
18021802
((Noteworthy Light) (Ascii Latin Greek Cyrillic mono=no sans=yes slant=0 italic=no case=mixed regular=no ex=89 em=151 lvw=12 lhw=8 uvw=11 uhw=11 fillp=31 vcnt=36 lasprat=78 pasprat=69 loasc=161 lodes=166 dides=108))
1803+
((Noto\ Color\ Emoji Regular) ())
18031804
((Noto\ Sans Bold) (Ascii Latin mono=no sans=yes slant=0 italic=no case=mixed regular=yes ex=91 em=172 lvw=27 lhw=21 uvw=28 uhw=24 fillp=57 vcnt=55 lasprat=104 pasprat=88 loasc=140 lodes=143 dides=102))
18041805
((Noto\ Sans Bold\ Italic) (Ascii Latin mono=no sans=yes slant=22 italic=yes case=mixed regular=yes ex=91 em=161 lvw=27 lhw=23 uvw=27 uhw=24 fillp=48 vcnt=56 lasprat=97 pasprat=97 loasc=140 lodes=143 dides=102))
18051806
((Noto\ Sans Italic) (Ascii Latin mono=no sans=yes slant=21 italic=yes case=mixed regular=yes ex=90 em=155 lvw=15 lhw=13 uvw=16 uhw=15 fillp=34 vcnt=37 lasprat=92 pasprat=87 loasc=142 lodes=144 dides=102))

TeXmacs/fonts/font-database.scm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,6 +1816,7 @@
18161816
((Norasi Regular) ((Norasi.ttf 0 104448) (Norasi.otf 0 62392)))
18171817
((Noteworthy Bold) ((Noteworthy.ttc 1 551924) (Noteworthy.ttc 1 427736) (Noteworthy.ttc 1 560844)))
18181818
((Noteworthy Light) ((Noteworthy.ttc 0 551924) (Noteworthy.ttc 0 427736) (Noteworthy.ttc 0 560844)))
1819+
((Noto\ Color\ Emoji Regular) ((NotoColorEmoji-Regular.ttf 0 24270832)))
18191820
((Noto\ Sans Bold) ((NotoSans-Bold.ttf 0 455164)))
18201821
((Noto\ Sans Bold\ Italic) ((NotoSans-BoldItalic.ttf 0 471004)))
18211822
((Noto\ Sans Italic) ((NotoSans-Italic.ttf 0 470472)))

TeXmacs/tests/tmu/206_2.tmu

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<TMU|<tuple|1.1.0|2025.0.11>>
2+
3+
<style|<tuple|generic|chinese|number-europe>>
4+
5+
<\body>
6+
🫡😇🙃😚🥰😛😐😪
7+
8+
🐔😔☀💞⛔🤦
9+
</body>
10+
11+
<\initial>
12+
<\collection>
13+
<associate|font|Noto Color Emoji>
14+
<associate|font-family|rm>
15+
<associate|page-medium|paper>
16+
<associate|page-screen-margin|false>
17+
</collection>
18+
</initial>

devel/206_2.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# [206_2] 初步支持emoji渲染
2+
## 2025/07/11
3+
### 如何测试
4+
打开 TeXmacs/tests/tmu/206_2.tmu 即可
5+
### What
6+
7+
初步支持emoji渲染。粘贴emoji字符后手动切换字体为Noto Color Emoji可以渲染
8+
9+
### How
10+
修改了unicode,unicode_font,qt_render等相关文件
11+
12+
### Why
13+
14+
新增emoji渲染功能

src/Data/String/unicode.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,23 @@ get_unicode_range (string c) {
2525
if (pos == N (uc)) return range;
2626
return "";
2727
}
28+
29+
bool
30+
is_emoji_character (unsigned int uc) {
31+
return (uc >= 0x1F600 && uc <= 0x1F64F) || // Emoticons
32+
(uc >= 0x1F300 && uc <= 0x1F5FF) || // Misc Symbols and Pictographs
33+
(uc >= 0x1F680 && uc <= 0x1F6FF) || // Transport and Map Symbols
34+
(uc >= 0x1F700 && uc <= 0x1F77F) || // Alchemical Symbols
35+
(uc >= 0x1F780 && uc <= 0x1F7FF) || // Geometric Shapes Extended
36+
(uc >= 0x1F800 && uc <= 0x1F8FF) || // Supplemental Arrows-C
37+
(uc >= 0x1F900 &&
38+
uc <= 0x1F9FF) || // Supplemental Symbols and Pictographs
39+
(uc >= 0x1FA00 && uc <= 0x1FA6F) || // Chess Symbols
40+
(uc >= 0x1FA70 &&
41+
uc <= 0x1FAFF) || // Symbols and Pictographs Extended-A
42+
(uc >= 0x2600 && uc <= 0x26FF) || // Miscellaneous Symbols
43+
(uc >= 0x2700 && uc <= 0x27BF) || // Dingbats
44+
(uc >= 0xFE00 && uc <= 0xFE0F) || // Variation Selectors
45+
(uc >= 0x1F1E6 && uc <= 0x1F1FF) || // Regional Indicator Symbols
46+
uc == 0x200D; // Zero Width Joiner
47+
}

src/Data/String/unicode.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@
1717
#include "string.hpp"
1818

1919
string get_unicode_range (string c);
20+
bool is_emoji_character (unsigned int uc);
2021

2122
#endif

src/Plugins/Freetype/tt_face.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
#include "analyze.hpp"
1414
#include "file.hpp"
1515
#include "font.hpp"
16+
#include "renderer.hpp"
1617
#include "tm_debug.hpp"
1718
#include "tm_timer.hpp"
1819
#include "tm_url.hpp"
1920
#include "tt_file.hpp"
21+
#include "unicode.hpp"
2022

2123
/******************************************************************************
2224
* Utilities
@@ -124,14 +126,26 @@ tt_font_metric_rep::get (int i) {
124126
fnm (i) = (pointer) M;
125127
int w = slot->bitmap.width;
126128
int h = slot->bitmap.rows;
127-
SI ww = w * PIXEL;
128-
SI hh = h * PIXEL;
129129
SI xw = tt_si (slot->metrics.width);
130130
SI xh = tt_si (slot->metrics.height);
131131
SI dx = tt_si (slot->metrics.horiBearingX);
132132
SI dy = tt_si (slot->metrics.horiBearingY);
133133
SI ll = tt_si (slot->metrics.horiAdvance);
134134
(void) xw;
135+
136+
bool is_emoji= is_emoji_character (i);
137+
if (is_emoji) {
138+
w= (ll + PIXEL / 2) / PIXEL; // Convert the horizontal advance to pixel
139+
// units and round to the nearest integer
140+
h = w; // treating Emojis as Squares
141+
xw= w * PIXEL; // Logical width in pixels
142+
xh= h * PIXEL; // Logical height in pixels
143+
dx= 0;
144+
dy= h * PIXEL;
145+
}
146+
147+
SI ww= w * PIXEL;
148+
SI hh= h * PIXEL;
135149
M->x1= 0;
136150
M->y1= dy - xh;
137151
M->x2= ll;
@@ -219,6 +233,21 @@ tt_font_glyphs_rep::get (int i) {
219233
}
220234
buf+= pitch;
221235
}
236+
237+
// Handle emoji characters
238+
bool is_emoji= is_emoji_character (i);
239+
if (is_emoji) {
240+
SI ll = tt_si (slot->metrics.horiAdvance);
241+
int emoji_w = (ll + PIXEL / 2) / PIXEL;
242+
int emoji_h = emoji_w; // treating Emojis as Squares
243+
int emoji_ox= 0;
244+
int emoji_oy= emoji_h;
245+
246+
// Recreate glyph with emoji dimensions
247+
G = glyph (emoji_w, emoji_h, -emoji_ox, emoji_oy);
248+
G->lwidth= emoji_w; // For emoji, logical width equals the adjusted width
249+
}
250+
222251
// cout << "Glyph " << i << " of " << res_name << "\n";
223252
// cout << G << "\n";
224253
if (G->width * G->height == 0) G= error_glyph;

src/Plugins/Qt/qt_renderer.cpp

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,24 @@
1010
******************************************************************************/
1111

1212
#include "qt_renderer.hpp"
13+
#include "Freetype/tt_face.hpp"
14+
#include "Freetype/unicode_font.hpp"
1315
#include "analyze.hpp"
1416
#include "file.hpp"
1517
#include "frame.hpp"
1618
#include "image_files.hpp"
19+
#include "picture.hpp"
20+
#include "qimage.h"
21+
#include "qt_picture.hpp"
1722
#include "qt_utilities.hpp"
1823
#include "scheme.hpp"
24+
#include "unicode.hpp"
1925

2026
#include <QObject>
2127
#include <QPaintDevice>
2228
#include <QPainterPath>
2329
#include <QPixmap>
30+
#include <QSvgRenderer>
2431
#include <QWidget>
2532

2633
/******************************************************************************
@@ -549,6 +556,10 @@ qt_renderer_rep::draw_bis (int c, font_glyphs fng, SI x, SI y) {
549556

550557
void
551558
qt_renderer_rep::draw (int c, font_glyphs fng, SI x, SI y) {
559+
if (is_emoji_character (c)) {
560+
draw_emoji (c, fng, x, y);
561+
return;
562+
}
552563
if (pen->get_type () == pencil_brush) {
553564
draw_bis (c, fng, x, y);
554565
return;
@@ -641,6 +652,74 @@ qt_renderer_rep::draw (const QFont& qfn, const QString& qs, SI x, SI y,
641652
painter->resetTransform ();
642653
}
643654

655+
picture
656+
svg_string_to_picture (string svg_data, int w, int h) {
657+
if (N (svg_data) == 0) {
658+
// Return null if no SVG data
659+
return picture ();
660+
}
661+
662+
// Create QByteArray from SVG string data
663+
QByteArray svg_bytes (as_charp (svg_data), N (svg_data));
664+
665+
// Create SVG renderer from the data
666+
QSvgRenderer renderer (svg_bytes);
667+
668+
if (!renderer.isValid ()) {
669+
// Return null if SVG is invalid
670+
return picture ();
671+
}
672+
673+
// Create QImage to render SVG into
674+
QImage image (w, h, QImage::Format_ARGB32);
675+
image.fill (Qt::transparent);
676+
677+
// Render SVG to the image
678+
QPainter painter (&image);
679+
renderer.render (&painter);
680+
painter.end ();
681+
682+
// Return the picture created from QImage
683+
return qt_picture (image, 0, 0);
684+
}
685+
686+
void
687+
qt_renderer_rep::draw_emoji (int char_code, font_glyphs fn, SI x, SI y) {
688+
689+
// Cast to TrueType font glyphs representation
690+
auto tt_font= static_cast<tt_font_glyphs_rep*> (fn.rep);
691+
if (is_nil (tt_font->face) || is_nil (tt_font->face->svg_table)) {
692+
return;
693+
}
694+
695+
// Get glyph ID for the emoji character
696+
int glyph_id= ft_get_char_index (tt_font->face->ft_face, char_code);
697+
if (glyph_id <= 0) {
698+
return;
699+
}
700+
701+
// Retrieve SVG data for the glyph
702+
string svg_data= tt_font->face->svg_table->get_svg_from_glyphid (glyph_id);
703+
if (N (svg_data) == 0) {
704+
return;
705+
}
706+
707+
// Calculate emoji size
708+
SI xo, yo;
709+
glyph pre_gl= fn->get (char_code);
710+
glyph gl = shrink (pre_gl, std_shrinkf, std_shrinkf, xo, yo);
711+
int w= gl->width, h= gl->height;
712+
713+
// Create picture from SVG data
714+
picture emoji_picture= svg_string_to_picture (svg_data, w, h);
715+
if (is_nil (emoji_picture)) {
716+
return;
717+
}
718+
719+
// Draw the emoji picture at the specified position
720+
draw_picture (emoji_picture, x, y, 255);
721+
}
722+
644723
/******************************************************************************
645724
* main qt renderer
646725
******************************************************************************/
@@ -904,4 +983,4 @@ qt_shadow_renderer_rep::get_shadow (renderer ren, SI x1, SI y1, SI x2, SI y2) {
904983
else {
905984
shadow->painter->setClipRect (QRect ());
906985
}
907-
}
986+
}

src/Plugins/Qt/qt_renderer.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "QTMPixmapOrImage.hpp"
1616
#include "basic_renderer.hpp"
17+
#include "qimage.h"
1718
#include <QImage>
1819
#include <QPainter>
1920
#include <QPixmap>
@@ -45,6 +46,7 @@ class qt_renderer_rep : public basic_renderer_rep {
4546
void draw_bis (int char_code, font_glyphs fn, SI x, SI y);
4647
void draw (int char_code, font_glyphs fn, SI x, SI y);
4748
void draw (const QFont& qfn, const QString& s, SI x, SI y, double zoom);
49+
void draw_emoji (int char_code, font_glyphs fn, SI x, SI y);
4850
void set_pencil (pencil p);
4951
void set_brush (brush b);
5052
void line (SI x1, SI y1, SI x2, SI y2);
@@ -71,6 +73,7 @@ class qt_renderer_rep : public basic_renderer_rep {
7173

7274
qt_renderer_rep* the_qt_renderer ();
7375
QImage* get_image (url u, int w, int h, tree eff, SI pixel);
76+
picture svg_string_to_picture (string svg_data, int w, int h);
7477

7578
class qt_shadow_renderer_rep : public qt_renderer_rep {
7679
public:

0 commit comments

Comments
 (0)