//+----------------------------------------------------------------------------+ //| Description: Magic Set Editor - Program to make Magic (tm) cards | //| Copyright: (C) 2001 - 2008 Twan van Laarhoven and "coppro" | //| License: GNU General Public License 2 or later (see file COPYING) | //+----------------------------------------------------------------------------+ // ----------------------------------------------------------------------------- : Includes #include #include #include #include #include DECLARE_TYPEOF_COLLECTION(TextElementP); DECLARE_POINTER_TYPE(FontTextElement); // ----------------------------------------------------------------------------- : TextElements void TextElements::draw(RotatedDC& dc, double scale, const RealRect& rect, const double* xs, DrawWhat what, size_t start, size_t end) const { FOR_EACH_CONST(e, elements) { size_t start_ = max(start, e->start); size_t end_ = min(end, e->end); if (start_ < end_) { e->draw(dc, scale, RealRect(rect.x + xs[start_-start] - xs[0], rect.y, xs[end_-start] - xs[start_-start], rect.height), xs + start_ - start, what, start_, end_); } if (end <= e->end) return; // nothing can be after this } } void TextElements::getCharInfo(RotatedDC& dc, double scale, size_t start, size_t end, vector& out) const { FOR_EACH_CONST(e, elements) { // characters before this element, after the previous while (out.size() < e->start) { out.push_back(CharInfo()); } e->getCharInfo(dc, scale, out); } while (out.size() < end) { out.push_back(CharInfo()); } } double TextElements::minScale() const { double m = 0.0001; FOR_EACH_CONST(e, elements) { m = max(m, e->minScale()); } return m; } double TextElements::scaleStep() const { double m = 1; FOR_EACH_CONST(e, elements) { m = min(m, e->scaleStep()); } return m; } // Colors for tags AColor param_colors[] = { AColor(0,170,0) , AColor(0,0,200) , AColor(200,0,100) , AColor(200,200,0) , AColor(0,170,170) , AColor(200,0,0) }; const size_t param_colors_count = sizeof(param_colors) / sizeof(param_colors[0]); // Helper class for TextElements::fromString, to allow persistent formating state accross recusive calls struct TextElementsFromString { // What formatting is enabled? int bold, italic, symbol; int soft, kwpph, param, line, soft_line; int code, code_kw, code_string, param_ref, error; int param_id; vector colors; vector sizes; /// put angle brackets around the text? bool bracket; TextElementsFromString() : bold(0), italic(0), symbol(0), soft(0), kwpph(0), param(0), line(0), soft_line(0) , code(0), code_kw(0), code_string(0), param_ref(0), error(0) , param_id(0), bracket(false) {} // read TextElements from a string void fromString(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) { te.elements.clear(); end = min(end, text.size()); size_t text_start = start; // for each character... for (size_t pos = start ; pos < end ; ) { Char c = text.GetChar(pos); if (c == _('<')) { if (text_start < pos) { // text element before this tag? addText(te, text, text_start, pos, style, ctx); } size_t tag_start = pos; pos = skip_tag(text, tag_start); if (is_substr(text, tag_start, _( ":"), tag_start); if (colon < pos - 1 && text.GetChar(colon) == _(':')) { AColor c = parse_acolor(text.substr(colon+1, pos-colon-2)); if (!c.Ok()) c = style.font.color; colors.push_back(c); } } else if (is_substr(text, tag_start, _(":"), tag_start); if (colon < pos - 1 && text.GetChar(colon) == _(':')) { double size = style.font.size; String v = text.substr(colon+1, pos-colon-2); v.ToDouble(&size); sizes.push_back(size); } } else if (is_substr(text, tag_start, _(" if (pos != String::npos) { String ref = text.substr(tag_start + 10, pos - tag_start - 11); long ref_n; if (ref.ToLong(&ref_n)) { param_id = (ref_n - 1)%param_colors_count + param_colors_count; } } param_ref += 1; } else if (is_substr(text, tag_start, _(" e(new AtomTextElement(pos, end_tag)); fromString(e->elements, text, pos, end_tag, style, ctx); te.elements.push_back(e); pos = skip_tag(text, end_tag); } else if (is_substr(text, tag_start, _( " e(new ErrorTextElement(pos, end_tag)); fromString(e->elements, text, pos, end_tag, style, ctx); te.elements.push_back(e); pos = skip_tag(text, end_tag); } else { // ignore other tags } text_start = pos; } else { pos += 1; } } if (text_start < end) { addText(te, text, text_start, end, style, ctx); } } private: /// Create a text element for a piece of text void addText(TextElements& te, const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) { String content = untag(text.substr(start, end - start)); assert(content.size() == end-start); // use symbol font? if (symbol > 0 && style.symbol_font.valid()) { te.elements.push_back(new_intrusive5(content, start, end, style.symbol_font, &ctx)); } else { // text, possibly mixed with symbols DrawWhat what = soft > 0 ? DRAW_ACTIVE : DRAW_NORMAL; LineBreak line_break = line > 0 ? BREAK_LINE : soft_line > 0 ? BREAK_SOFT : BREAK_HARD; if (kwpph > 0 || param > 0) { // bracket the text content = String(LEFT_ANGLE_BRACKET) + content + RIGHT_ANGLE_BRACKET; start -= 1; end += 1; } if (style.always_symbol && style.symbol_font.valid()) { // mixed symbols/text, autodetected by symbol font size_t text_pos = 0; size_t pos = 0; FontP font; while (pos < end-start) { if (size_t n = style.symbol_font.font->recognizePrefix(content,pos)) { // at 'pos' there are n symbol font characters if (text_pos < pos) { // text before it? if (!font) font = makeFont(style); te.elements.push_back(new_intrusive6(content.substr(text_pos, pos-text_pos), start+text_pos, start+pos, font, what, line_break)); } te.elements.push_back(new_intrusive5(content.substr(pos,n), start+pos, start+pos+n, style.symbol_font, &ctx)); text_pos = pos += n; } else { ++pos; } } if (text_pos < pos) { if (!font) font = makeFont(style); te.elements.push_back(new_intrusive6(content.substr(text_pos), start+text_pos, end, font, what, line_break)); } } else { te.elements.push_back(new_intrusive6(content, start, end, makeFont(style), what, line_break)); } } } FontP makeFont(const TextStyle& style) { return style.font.make( (bold > 0 ? FONT_BOLD : FONT_NORMAL) | (italic > 0 ? FONT_ITALIC : FONT_NORMAL) | (soft > 0 ? FONT_SOFT : FONT_NORMAL) | (kwpph > 0 ? FONT_SOFT : FONT_NORMAL) | (code > 0 ? FONT_CODE : FONT_NORMAL) | (code_kw > 0 ? FONT_CODE_KW : FONT_NORMAL) | (code_string > 0 ? FONT_CODE_STRING : FONT_NORMAL), param > 0 || param_ref > 0 ? ¶m_colors[(param_id++) % param_colors_count] : !colors.empty() ? &colors.back() : nullptr, !sizes.empty() ? &sizes.back() : nullptr ); } }; void TextElements::fromString(const String& text, size_t start, size_t end, const TextStyle& style, Context& ctx) { TextElementsFromString f; f.fromString(*this, text, start, end, style, ctx); } /* // ----------------------------------------------------------------------------- : CompoundTextElement void CompoundTextElement::draw(RotatedDC& dc, double scale, const RealRect& rect, DrawWhat what, size_t start, size_t end) const { elements.draw(dc, scale, rect, what, start, end); } RealSize CompoundTextElement::charSize(RotatedDC& dc, double scale, size_t index) const { return elements.charSize(rot, scale, index); } */