2 * \file InsetSpecialChar.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup Nielsen
7 * \author Jean-Marc Lasgouttes
8 * \author Lars Gullik Bjønnes
10 * Full author contact details are available in file CREDITS.
15 #include "InsetSpecialChar.h"
17 #include "Dimension.h"
21 #include "LaTeXFeatures.h"
23 #include "MetricsInfo.h"
24 #include "output_xhtml.h"
25 #include "texstream.h"
27 #include "frontends/FontMetrics.h"
28 #include "frontends/NullPainter.h"
29 #include "frontends/Painter.h"
31 #include "support/debug.h"
32 #include "support/docstream.h"
39 InsetSpecialChar::InsetSpecialChar(Kind k)
44 InsetSpecialChar::Kind InsetSpecialChar::kind() const
50 docstring InsetSpecialChar::toolTip(BufferView const &, int, int) const
55 message = from_ascii("Optional Line Break (ZWSP)");
58 message = from_ascii("Ligature Break (ZWNJ)");
61 message = from_ascii("End of Sentence");
64 message = from_ascii("Hyphenation Point");
67 message = from_ascii("Breakable Slash");
70 message = from_ascii("Protected Hyphen (SHY)");
78 // no tooltip for these ones.
87 // helper function: draw text and update x.
88 void drawChar(PainterInfo & pi, int & x, int const y, char_type ch)
90 FontInfo font = pi.base.font;
91 font.setPaintColor(pi.textColor(font.realColor()));
92 pi.pain.text(x, y, ch, font);
93 x += theFontMetrics(font).width(ch);
97 void drawLogo(PainterInfo & pi, int & x, int const y, InsetSpecialChar::Kind kind)
99 FontInfo const & font = pi.base.font;
100 int const em = theFontMetrics(font).em();
102 case InsetSpecialChar::PHRASE_LYX:
104 * \providecommand{\LyX}{L\kern-.1667em\lower.25em\hbox{Y}\kern-.125emX\\@};
106 drawChar(pi, x, y, 'L');
108 drawChar(pi, x, y + em / 4, 'Y');
110 drawChar(pi, x, y, 'X');
113 case InsetSpecialChar::PHRASE_TEX: {
115 * \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@}
117 int const ex = theFontMetrics(font).xHeight();
118 drawChar(pi, x, y, 'T');
120 drawChar(pi, x, y + ex / 2, 'E');
122 drawChar(pi, x, y, 'X');
125 case InsetSpecialChar::PHRASE_LATEX2E:
127 * \DeclareRobustCommand{\LaTeXe}{\mbox{\m@th
128 * \if b\expandafter\@car\f@series\@nil\boldmath\fi
129 * \LaTeX\kern.15em2$_{\textstyle\varepsilon}$}}
131 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_LATEX);
133 drawChar(pi, x, y, '2');
134 // ε U+03B5 GREEK SMALL LETTER EPSILON
135 drawChar(pi, x, y + em / 4, char_type(0x03b5));
138 case InsetSpecialChar::PHRASE_LATEX: {
140 * \DeclareRobustCommand{\LaTeX}{L\kern-.36em%
142 * \vbox to\ht\z@{\hbox{\check@mathfonts
143 * \fontsize\sf@size\z@
144 * \math@fontsfalse\selectfont
151 drawChar(pi, x, y, 'L');
153 PainterInfo pi2 = pi;
154 pi2.base.font.decSize().decSize();
155 drawChar(pi2, x, y - em / 5, 'A');
157 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_TEX);
161 LYXERR0("No information for drawing logo " << kind);
168 void InsetSpecialChar::metrics(MetricsInfo & mi, Dimension & dim) const
170 frontend::FontMetrics const & fm =
171 theFontMetrics(mi.base.font);
172 dim.asc = fm.maxAscent();
179 dim.asc = fm.xHeight();
180 dim.des = fm.descent('g');
181 dim.wid = fm.em() / 8;
186 case END_OF_SENTENCE:
190 s = from_ascii(". . .");
193 // ▹ U+25B9 WHITE RIGHT-POINTING SMALL TRIANGLE
194 // There is a \thinspace on each side of the triangle
195 dim.wid = 2 * fm.em() / 6 + fm.width(char_type(0x25B9));
198 dim.wid = fm.width(from_ascii("-"));
200 dim.wid -= 2; // to make it look shorter
204 dim.des = fm.descent(s[0]);
213 dim.asc = fm.maxAscent();
214 dim.des = fm.maxDescent();
215 frontend::NullPainter np;
216 PainterInfo pi(mi.base.bv, np);
217 pi.base.font = mi.base.font;
218 drawLogo(pi, dim.wid, 0, kind_);
222 dim.wid = fm.width(s);
226 void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const
228 FontInfo font = pi.base.font;
233 font.setColor(Color_special);
234 pi.pain.text(x, y, char_type('-'), font);
239 // A small vertical line
240 int const asc = theFontMetrics(pi.base.font).xHeight();
241 int const desc = theFontMetrics(pi.base.font).descent('g');
242 int const x0 = x; // x + 1; // FIXME: incline,
243 int const x1 = x; // x - 1; // similar to LibreOffice?
244 int const y0 = y + desc;
245 int const y1 = y - asc / 3;
246 pi.pain.line(x0, y1, x1, y0, Color_special);
251 font.setColor(Color_special);
252 pi.pain.text(x, y, char_type('|'), font);
255 case END_OF_SENTENCE:
257 font.setColor(Color_special);
258 pi.pain.text(x, y, char_type('.'), font);
263 font.setColor(Color_special);
264 string ell = ". . . ";
265 docstring dell(ell.begin(), ell.end());
266 pi.pain.text(x, y, dell, font);
271 frontend::FontMetrics const & fm =
272 theFontMetrics(font);
274 // There is a \thinspace on each side of the triangle
276 // ▹ U+25B9 WHITE RIGHT-POINTING SMALL TRIANGLE
277 // ◃ U+25C3 WHITE LEFT-POINTING SMALL TRIANGLE
278 char_type const c = pi.ltr_pos ? 0x25B9 : 0x25C3;
279 font.setColor(Color_special);
280 pi.pain.text(x, y, c, font);
285 font.setColor(Color_special);
286 pi.pain.text(x, y, char_type('/'), font);
291 font.setColor(Color_latex);
292 pi.pain.text(x, y, char_type('-'), font);
299 drawLogo(pi, x, y, kind_);
305 void InsetSpecialChar::write(ostream & os) const
310 command = "softhyphen";
313 command = "allowbreak";
316 command = "ligaturebreak";
318 case END_OF_SENTENCE:
319 command = "endofsentence";
325 command = "menuseparator";
328 command = "breakableslash";
331 command = "nobreakdash";
346 os << "\\SpecialChar " << command << "\n";
350 void InsetSpecialChar::read(Lexer & lex)
353 string const command = lex.getString();
355 if (command == "softhyphen")
357 else if (command == "allowbreak")
359 else if (command == "ligaturebreak")
360 kind_ = LIGATURE_BREAK;
361 else if (command == "endofsentence")
362 kind_ = END_OF_SENTENCE;
363 else if (command == "ldots")
365 else if (command == "menuseparator")
366 kind_ = MENU_SEPARATOR;
367 else if (command == "breakableslash")
369 else if (command == "nobreakdash")
371 else if (command == "LyX")
373 else if (command == "TeX")
375 else if (command == "LaTeX2e")
376 kind_ = PHRASE_LATEX2E;
377 else if (command == "LaTeX")
378 kind_ = PHRASE_LATEX;
380 lex.printError("InsetSpecialChar: Unknown kind: `$$Token'");
384 void InsetSpecialChar::latex(otexstream & os,
385 OutputParams const & rp) const
387 bool const rtl = rp.local_font->isRightToLeft();
388 bool const utf8 = rp.encoding->iconvName() == "UTF-8";
390 string lswitche = "";
391 if (rtl && !rp.use_polyglossia) {
394 if (rp.local_font->language()->lang() == "arabic_arabi"
395 || rp.local_font->language()->lang() == "farsi")
396 lswitch = "\\textLR{";
404 // U+200B not yet supported by utf8 inputenc
405 os << "\\LyXZeroWidthSpace" << termcmd;
409 // U+200C ZERO WIDTH NON-JOINER
412 os << "\\textcompwordmark" << termcmd;
414 case END_OF_SENTENCE:
418 os << "\\ldots" << termcmd;
428 os << "\\slash" << termcmd;
433 os << "\\nobreakdash-";
438 os << lswitch << "\\LyX" << termcmd << lswitche;
443 os << lswitch << "\\TeX" << termcmd << lswitche;
448 os << lswitch << "\\LaTeXe" << termcmd << lswitche;
453 os << lswitch << "\\LaTeX" << termcmd << lswitche;
459 int InsetSpecialChar::plaintext(odocstringstream & os,
460 OutputParams const &, size_t) const
466 // U+200B ZERO WIDTH SPACE (ZWSP)
470 // U+200C ZERO WIDTH NON-JOINER
473 case END_OF_SENTENCE:
477 // … U+2026 HORIZONTAL ELLIPSIS
487 // ‑ U+2011 NON-BREAKING HYPHEN
498 // ε U+03B5 GREEK SMALL LETTER EPSILON
509 int InsetSpecialChar::docbook(odocstream & os, OutputParams const &) const
515 // U+200B ZERO WIDTH SPACE (ZWSP)
520 case END_OF_SENTENCE:
543 // ε U+03B5 GREEK SMALL LETTER EPSILON
554 docstring InsetSpecialChar::xhtml(XHTMLStream & xs, OutputParams const &) const
560 xs << XHTMLStream::ESCAPE_NONE << "​";
563 xs << XHTMLStream::ESCAPE_NONE << "‌";
565 case END_OF_SENTENCE:
569 xs << XHTMLStream::ESCAPE_NONE << "…";
572 xs << XHTMLStream::ESCAPE_NONE << "⇒";
575 xs << XHTMLStream::ESCAPE_NONE << "⁄";
578 xs << XHTMLStream::ESCAPE_NONE << "‑";
587 xs << "LaTeX2" << XHTMLStream::ESCAPE_NONE << "ε";
597 void InsetSpecialChar::toString(odocstream & os) const
602 // Do not output ZERO WIDTH SPACE and ZERO WIDTH NON JOINER here
603 // Spell checker would choke on it.
608 odocstringstream ods;
609 plaintext(ods, OutputParams(0));
614 void InsetSpecialChar::forOutliner(docstring & os, size_t const,
617 odocstringstream ods;
618 plaintext(ods, OutputParams(0));
623 void InsetSpecialChar::validate(LaTeXFeatures & features) const
625 if (kind_ == ALLOWBREAK)
626 features.require("lyxzerowidthspace");
627 if (kind_ == MENU_SEPARATOR)
628 features.require("lyxarrow");
629 if (kind_ == NOBREAKDASH)
630 features.require("amsmath");
631 if (kind_ == PHRASE_LYX)
632 features.require("LyX");
636 bool InsetSpecialChar::isChar() const
638 return kind_ != HYPHENATION && kind_ != LIGATURE_BREAK;
642 bool InsetSpecialChar::isLetter() const
644 return kind_ == HYPHENATION || kind_ == LIGATURE_BREAK
645 || kind_ == NOBREAKDASH;
649 bool InsetSpecialChar::isLineSeparator() const
652 // this would be nice, but it does not work, since
653 // Paragraph::stripLeadingSpaces nukes the characters which
654 // have this property. I leave the code here, since it should
655 // eventually be made to work. (JMarc 20020327)
656 return kind_ == HYPHENATION || kind_ == ALLOWBREAK
657 || kind_ == MENU_SEPARATOR || kind_ == SLASH;