10 #include "../../stdafx.h"
11 #include "../../debug.h"
13 #include "../../language.h"
14 #include "../../strings_func.h"
15 #include "../../string_func.h"
16 #include "../../table/control_codes.h"
17 #include "../../zoom_func.h"
23 #include "../../safeguards.h"
26 # pragma comment(lib, "usp10")
42 std::vector<GlyphID> ft_glyphs;
45 std::vector<WORD> char_to_glyph;
47 std::vector<SCRIPT_VISATTR> vis_attribs;
48 std::vector<WORD> glyphs;
49 std::vector<int> advances;
50 std::vector<GOFFSET> offsets;
53 UniscribeRun(
int pos,
int len,
Font *font, SCRIPT_ANALYSIS &sa) : pos(pos), len(len), font(font), sa(sa) {}
76 std::vector<GlyphID> glyphs;
77 std::vector<Position> positions;
78 std::vector<WORD> char_to_glyph;
85 mutable std::vector<int> glyph_to_char;
91 std::span<const GlyphID> GetGlyphs()
const override {
return this->glyphs; }
92 std::span<const Position> GetPositions()
const override {
return this->positions; }
93 std::span<const int> GetGlyphToCharMap()
const override;
95 const Font *GetFont()
const override {
return this->font; }
96 int GetLeading()
const override {
return this->font->
fc->
GetHeight(); }
97 int GetGlyphCount()
const override {
return this->num_glyphs; }
98 int GetAdvance()
const {
return this->total_advance; }
106 int CountRuns()
const override {
return (uint)this->size(); }
107 const VisualRun &GetVisualRun(
int run)
const override {
return this->at(run); }
109 int GetInternalCharLength(char32_t c)
const override
112 return c >= 0x010000U ? 2 : 1;
123 void Reflow()
override
125 this->cur_range = this->ranges.begin();
126 this->cur_range_offset = 0;
129 std::unique_ptr<const Line> NextLine(
int max_width)
override;
132 void UniscribeResetScriptCache(
FontSize size)
143 if (font->
fc->
GetOSHandle() !=
nullptr)
return CreateFontIndirect(
reinterpret_cast<PLOGFONT
>(
const_cast<void *
>(font->
fc->
GetOSHandle())));
146 ZeroMemory(&logfont,
sizeof(LOGFONT));
148 logfont.lfWeight = FW_NORMAL;
149 logfont.lfCharSet = DEFAULT_CHARSET;
152 return CreateFontIndirect(&logfont);
159 range.glyphs.resize(range.len * 3 / 2 + 16);
160 range.vis_attribs.resize(range.glyphs.size());
163 range.char_to_glyph.resize(range.len);
165 HDC temp_dc =
nullptr;
166 HFONT old_font =
nullptr;
167 HFONT cur_font =
nullptr;
172 HRESULT hr = ScriptShape(temp_dc, &
_script_cache[range.font->
fc->
GetSize()], buff + range.pos, range.len, (
int)range.glyphs.size(), &range.sa, &range.glyphs[0], &range.char_to_glyph[0], &range.vis_attribs[0], &glyphs_used);
175 range.glyphs.resize(glyphs_used);
176 range.vis_attribs.resize(glyphs_used);
180 range.advances.resize(range.glyphs.size());
181 range.offsets.resize(range.glyphs.size());
182 hr = ScriptPlace(temp_dc, &
_script_cache[range.font->
fc->
GetSize()], &range.glyphs[0], (
int)range.glyphs.size(), &range.vis_attribs[0], &range.sa, &range.advances[0], &range.offsets[0], &abc);
186 range.ft_glyphs.resize(range.glyphs.size());
187 for (
size_t g_id = 0; g_id < range.glyphs.size(); g_id++) {
188 range.ft_glyphs[g_id] = range.glyphs[g_id];
190 for (
int i = 0; i < range.len; i++) {
191 if (buff[range.pos + i] >= SCC_SPRITE_START && buff[range.pos + i] <= SCC_SPRITE_END) {
192 auto pos = range.char_to_glyph[i];
193 if (range.ft_glyphs[pos] == 0) {
196 range.advances[pos] = range.font->
fc->
GetGlyphWidth(range.ft_glyphs[pos]);
201 range.total_advance = 0;
202 for (
size_t i = 0; i < range.advances.size(); i++) {
205 if (range.advances[i] > 0 && range.ft_glyphs[i] != 0xFFFF) range.advances[i] = range.font->
fc->
GetGlyphWidth(range.ft_glyphs[i]);
207 range.total_advance += range.advances[i];
213 if (hr == E_OUTOFMEMORY) {
215 range.glyphs.resize(range.glyphs.size() * 2);
216 range.vis_attribs.resize(range.vis_attribs.size() * 2);
217 }
else if (hr == E_PENDING) {
220 if (cur_font ==
nullptr)
return false;
222 temp_dc = CreateCompatibleDC(
nullptr);
223 SetMapMode(temp_dc, MM_TEXT);
224 old_font = (HFONT)SelectObject(temp_dc, cur_font);
225 }
else if (hr == USP_E_SCRIPT_NOT_IN_FONT && range.sa.eScript != SCRIPT_UNDEFINED) {
227 range.sa.eScript = SCRIPT_UNDEFINED;
230 if (temp_dc !=
nullptr) {
231 SelectObject(temp_dc, old_font);
232 DeleteObject(cur_font);
233 ReleaseDC(
nullptr, temp_dc);
239 if (temp_dc !=
nullptr) {
240 SelectObject(temp_dc, old_font);
241 DeleteObject(cur_font);
242 ReleaseDC(
nullptr, temp_dc);
251 SCRIPT_CONTROL control;
252 ZeroMemory(&control,
sizeof(SCRIPT_CONTROL));
256 ZeroMemory(&state,
sizeof(SCRIPT_STATE));
259 std::vector<SCRIPT_ITEM> items(16);
263 HRESULT hr = ScriptItemize(buff, length, (
int)items.size() - 1, &control, &state, &items[0], &generated);
267 items.resize(generated + 1);
271 if (hr != E_OUTOFMEMORY)
return std::vector<SCRIPT_ITEM>();
273 items.resize(items.size() * 2);
281 int32_t length = buff_end - buff;
283 if (length == 0)
return nullptr;
286 for (
auto const &pair : fontMapping) {
287 if (pair.second->fc->IsBuiltInFont())
return nullptr;
292 if (items.empty())
return nullptr;
296 std::vector<UniscribeRun> ranges;
299 std::vector<SCRIPT_ITEM>::iterator cur_item = items.begin();
300 for (
auto const &i : fontMapping) {
301 while (cur_pos < i.first && cur_item != items.end() - 1) {
303 int stop_pos = std::min(i.first, (cur_item + 1)->iCharPos);
304 assert(stop_pos - cur_pos > 0);
305 ranges.emplace_back(cur_pos, stop_pos - cur_pos, i.second, cur_item->a);
313 if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
321 std::unique_ptr<const ParagraphLayouter::Line> UniscribeParagraphLayout::NextLine(
int max_width)
323 std::vector<UniscribeRun>::iterator start_run = this->
cur_range;
324 std::vector<UniscribeRun>::iterator last_run = this->
cur_range;
326 if (start_run == this->ranges.end())
return nullptr;
330 if (this->cur_range_offset != 0) {
331 std::vector<int> dx(start_run->len);
332 ScriptGetLogicalWidths(&start_run->sa, start_run->len, (
int)start_run->glyphs.size(), &start_run->advances[0], &start_run->char_to_glyph[0], &start_run->vis_attribs[0], &dx[0]);
334 for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
341 while (last_run != this->ranges.end() && cur_width <= max_width) {
342 cur_width += last_run->total_advance;
347 int remaining_offset = (last_run - 1)->len + 1;
348 int whitespace_count = 0;
349 if (cur_width > max_width) {
350 std::vector<SCRIPT_LOGATTR> log_attribs;
353 int width_avail = max_width;
356 int last_cluster = this->cur_range_offset + 1;
357 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
358 log_attribs.resize(r->pos - start_run->pos + r->len);
359 if (FAILED(ScriptBreak(this->text_buffer + r->pos + start_offs, r->len - start_offs, &r->sa, &log_attribs[r->pos - start_run->pos + start_offs])))
return nullptr;
361 std::vector<int> dx(r->len);
362 ScriptGetLogicalWidths(&r->sa, r->len, (
int)r->glyphs.size(), &r->advances[0], &r->char_to_glyph[0], &r->vis_attribs[0], &dx[0]);
365 for (
int c = start_offs; c < r->len && width_avail > 0; c++, num_chars++) {
366 if (c > start_offs && log_attribs[num_chars].fCharStop) last_cluster = num_chars;
367 width_avail -= dx[c];
374 while (--num_chars > this->cur_range_offset && !log_attribs[num_chars].fSoftBreak && !log_attribs[num_chars].fWhiteSpace) {}
376 if (num_chars == this->cur_range_offset) {
378 num_chars = last_cluster;
382 while (num_chars - 1 > this->cur_range_offset && log_attribs[num_chars - 1].fWhiteSpace) num_chars--;
384 while (num_chars + whitespace_count < (
int)log_attribs.size() && log_attribs[num_chars + whitespace_count].fWhiteSpace) whitespace_count++;
387 for (std::vector<UniscribeRun>::iterator run = start_run; run != last_run; run++) {
388 num_chars -= run->len;
390 if (num_chars <= 0) {
391 remaining_offset = num_chars + run->len + 1;
393 assert(remaining_offset - 1 > 0);
400 std::vector<BYTE> bidi_level;
401 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
402 bidi_level.push_back(r->sa.s.uBidiLevel);
404 std::vector<INT> vis_to_log(bidi_level.size());
405 if (FAILED(ScriptLayout((
int)bidi_level.size(), &bidi_level[0], &vis_to_log[0],
nullptr)))
return nullptr;
408 std::unique_ptr<UniscribeLine> line = std::make_unique<UniscribeLine>();
411 for (std::vector<INT>::iterator l = vis_to_log.begin(); l != vis_to_log.end(); l++) {
412 std::vector<UniscribeRun>::iterator i_run = start_run + *l;
416 if (i_run == last_run - 1 && remaining_offset <= (last_run - 1)->len) {
417 run.len = remaining_offset - 1;
421 if (i_run == start_run && this->cur_range_offset > 0) {
422 assert(run.len - this->cur_range_offset > 0);
429 line->emplace_back(run, cur_pos);
430 cur_pos += run.total_advance;
433 if (remaining_offset + whitespace_count - 1 < (last_run - 1)->len) {
435 this->cur_range_offset = remaining_offset + whitespace_count - 1;
436 this->cur_range = last_run - 1;
437 assert(this->cur_range->len > this->cur_range_offset);
439 this->cur_range_offset = 0;
440 this->cur_range = last_run;
453 for (
const auto &run : *
this) {
454 leading = std::max(leading, run.GetLeading());
467 for (
const auto &run : *
this) {
468 length += run.GetAdvance();
474 UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(
const UniscribeRun &range,
int x) : glyphs(range.ft_glyphs), char_to_glyph(range.char_to_glyph), start_pos(range.pos), total_advance(range.total_advance), font(range.font)
476 this->num_glyphs = (int)glyphs.size();
477 this->positions.reserve(this->num_glyphs);
480 for (
int i = 0; i < this->num_glyphs; i++) {
481 int x_advance = range.advances[i];
482 this->positions.emplace_back(range.offsets[i].du + advance, range.offsets[i].du + advance + x_advance - 1, range.offsets[i].dv);
484 advance += x_advance;
488 UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(UniscribeVisualRun&& other) noexcept
489 : glyphs(std::move(other.glyphs)), positions(std::move(other.positions)), char_to_glyph(std::move(other.char_to_glyph)),
490 start_pos(other.start_pos), total_advance(other.total_advance), num_glyphs(other.num_glyphs), font(other.font),
491 glyph_to_char(std::move(other.glyph_to_char))
495 std::span<const int> UniscribeParagraphLayout::UniscribeVisualRun::GetGlyphToCharMap()
const
497 if (this->glyph_to_char.empty()) {
498 this->glyph_to_char.resize(this->GetGlyphCount());
502 for (
int c = 0; c < (int)this->char_to_glyph.size(); c++) {
504 if (this->glyph_to_char[this->char_to_glyph[c]] == 0) this->glyph_to_char[this->char_to_glyph[c]] = c + this->start_pos;
508 int last_char = this->glyph_to_char[0];
509 for (
int g = 0; g < this->GetGlyphCount(); g++) {
510 if (this->glyph_to_char[g] != 0) last_char = this->glyph_to_char[g];
511 this->glyph_to_char[g] = last_char;
515 return this->glyph_to_char;
521 const char *string_base = s;
523 this->utf16_to_utf8.clear();
524 this->str_info.clear();
529 std::vector<wchar_t> utf16_str;
531 size_t idx = s - string_base;
533 char32_t c = Utf8Consume(&s);
535 utf16_str.push_back((
wchar_t)c);
538 utf16_str.push_back((
wchar_t)(0xD800 + ((c - 0x10000) >> 10)));
539 utf16_str.push_back((
wchar_t)(0xDC00 + ((c - 0x10000) & 0x3FF)));
540 this->utf16_to_utf8.push_back(idx);
542 this->utf16_to_utf8.push_back(idx);
544 this->utf16_to_utf8.push_back(s - string_base);
547 this->str_info.resize(utf16_to_utf8.size());
549 if (!utf16_str.empty()) {
553 for (std::vector<SCRIPT_ITEM>::const_iterator run = runs.begin(); !runs.empty() && run != runs.end() - 1; run++) {
555 int len = (run + 1)->iCharPos - run->iCharPos;
556 std::vector<SCRIPT_LOGATTR> attr(len);
557 ScriptBreak(&utf16_str[run->iCharPos], len, &run->a, &attr[0]);
560 for (
size_t c = 0; c < attr.size(); c++) {
562 this->str_info[c + run->iCharPos].word_stop = attr[c].fWordStop || c == 0;
563 this->str_info[c + run->iCharPos].char_stop = attr[c].fCharStop;
569 this->str_info.back().char_stop =
true;
570 this->str_info.back().word_stop =
true;
576 size_t utf16_pos = 0;
577 for (
size_t i = 0; i < this->utf16_to_utf8.size(); i++) {
578 if (this->utf16_to_utf8[i] == pos) {
585 while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--;
586 this->cur_pos = utf16_pos;
588 return this->utf16_to_utf8[this->cur_pos];
593 assert(this->cur_pos <= this->utf16_to_utf8.size());
596 if (this->cur_pos == this->utf16_to_utf8.size())
return END;
600 }
while (this->cur_pos < this->utf16_to_utf8.size() && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop));
602 return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos];
607 assert(this->cur_pos <= this->utf16_to_utf8.size());
610 if (this->cur_pos == 0)
return END;
614 }
while (this->cur_pos > 0 && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop));
616 return this->utf16_to_utf8[this->cur_pos];