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"
20 #include "LaTeXFeatures.h"
22 #include "MetricsInfo.h"
23 #include "output_xhtml.h"
24 #include "texstream.h"
26 #include "frontends/FontMetrics.h"
27 #include "frontends/Painter.h"
29 #include "support/debug.h"
30 #include "support/docstream.h"
37 InsetSpecialChar::InsetSpecialChar(Kind k)
42 InsetSpecialChar::Kind InsetSpecialChar::kind() const
50 int logoWidth(FontInfo const & font, InsetSpecialChar::Kind kind) {
51 frontend::FontMetrics const & fm = theFontMetrics(font);
52 int const em = fm.em();
54 // See drawlogo() below to understand what this does.
56 case InsetSpecialChar::PHRASE_LYX:
57 width = fm.width(from_ascii("L")) - em / 6
58 + fm.width(from_ascii("Y")) - em / 8
59 + fm.width(from_ascii("X"));
62 case InsetSpecialChar::PHRASE_TEX:
63 width = fm.width(from_ascii("T")) - em / 6
64 + fm.width(from_ascii("E")) - em / 8
65 + fm.width(from_ascii("X"));
68 case InsetSpecialChar::PHRASE_LATEX2E:
69 width = logoWidth(font, InsetSpecialChar::PHRASE_LATEX)
71 + fm.width(from_ascii("2") + char_type(0x03b5));
73 case InsetSpecialChar::PHRASE_LATEX: {
74 FontInfo smaller = font;
75 smaller.decSize().decSize();
76 width = fm.width(from_ascii("L")) - 9 * em / 25
77 + theFontMetrics(smaller).width(from_ascii("A")) - 3 * em / 20
78 + logoWidth(font, InsetSpecialChar::PHRASE_TEX);
82 LYXERR0("No information for computing width of logo " << kind);
90 docstring InsetSpecialChar::toolTip(BufferView const &, int, int) const
95 message = from_ascii("Optional Line Break (ZWSP)");
98 message = from_ascii("Ligature Break (ZWNJ)");
100 case END_OF_SENTENCE:
101 message = from_ascii("End of Sentence");
104 message = from_ascii("Hyphenation Point");
107 message = from_ascii("Breakable Slash");
110 message = from_ascii("Protected Hyphen (SHY)");
118 // no tooltip for these ones.
125 void InsetSpecialChar::metrics(MetricsInfo & mi, Dimension & dim) const
127 frontend::FontMetrics const & fm =
128 theFontMetrics(mi.base.font);
129 dim.asc = fm.maxAscent();
136 dim.asc = fm.xHeight();
137 dim.des = fm.descent('g');
138 dim.wid = fm.em() / 8;
143 case END_OF_SENTENCE:
147 s = from_ascii(". . .");
150 // ▹ U+25B9 WHITE RIGHT-POINTING SMALL TRIANGLE
151 // There is a \thinspace on each side of the triangle
152 dim.wid = 2 * fm.em() / 6 + fm.width(char_type(0x25B9));
155 dim.wid = fm.width(from_ascii("-"));
157 dim.wid -= 2; // to make it look shorter
161 dim.des = fm.descent(s[0]);
170 dim.asc = fm.maxAscent();
171 dim.des = fm.maxDescent();
172 dim.wid = logoWidth(mi.base.font, kind_);
176 dim.wid = fm.width(s);
182 // helper function: draw text and update x.
183 void drawChar(PainterInfo & pi, int & x, int const y, char_type ch)
185 FontInfo font = pi.base.font;
186 font.setPaintColor(pi.textColor(font.realColor()));
187 pi.pain.text(x, y, ch, font);
188 x += theFontMetrics(font).width(ch);
192 void drawLogo(PainterInfo & pi, int & x, int const y, InsetSpecialChar::Kind kind)
194 FontInfo const & font = pi.base.font;
195 int const em = theFontMetrics(font).em();
197 case InsetSpecialChar::PHRASE_LYX:
199 * \providecommand{\LyX}{L\kern-.1667em\lower.25em\hbox{Y}\kern-.125emX\\@};
201 drawChar(pi, x, y, 'L');
203 drawChar(pi, x, y + em / 4, 'Y');
205 drawChar(pi, x, y, 'X');
208 case InsetSpecialChar::PHRASE_TEX: {
210 * \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@}
212 int const ex = theFontMetrics(font).xHeight();
213 drawChar(pi, x, y, 'T');
215 drawChar(pi, x, y + ex / 2, 'E');
217 drawChar(pi, x, y, 'X');
220 case InsetSpecialChar::PHRASE_LATEX2E:
222 * \DeclareRobustCommand{\LaTeXe}{\mbox{\m@th
223 * \if b\expandafter\@car\f@series\@nil\boldmath\fi
224 * \LaTeX\kern.15em2$_{\textstyle\varepsilon}$}}
226 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_LATEX);
228 drawChar(pi, x, y, '2');
229 drawChar(pi, x, y + em / 4, char_type(0x03b5));
232 case InsetSpecialChar::PHRASE_LATEX: {
234 * \DeclareRobustCommand{\LaTeX}{L\kern-.36em%
236 * \vbox to\ht\z@{\hbox{\check@mathfonts
237 * \fontsize\sf@size\z@
238 * \math@fontsfalse\selectfont
245 drawChar(pi, x, y, 'L');
247 PainterInfo pi2 = pi;
248 pi2.base.font.decSize().decSize();
249 drawChar(pi2, x, y - em / 5, 'A');
251 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_TEX);
255 LYXERR0("No information for drawing logo " << kind);
261 void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const
263 FontInfo font = pi.base.font;
268 font.setColor(Color_special);
269 pi.pain.text(x, y, char_type('-'), font);
274 // A small vertical line
275 int const asc = theFontMetrics(pi.base.font).xHeight();
276 int const desc = theFontMetrics(pi.base.font).descent('g');
277 int const x0 = x; // x + 1; // FIXME: incline,
278 int const x1 = x; // x - 1; // similar to LibreOffice?
279 int const y0 = y + desc;
280 int const y1 = y - asc / 3;
281 pi.pain.line(x0, y1, x1, y0, Color_special);
286 font.setColor(Color_special);
287 pi.pain.text(x, y, char_type('|'), font);
290 case END_OF_SENTENCE:
292 font.setColor(Color_special);
293 pi.pain.text(x, y, char_type('.'), font);
298 font.setColor(Color_special);
299 string ell = ". . . ";
300 docstring dell(ell.begin(), ell.end());
301 pi.pain.text(x, y, dell, font);
306 frontend::FontMetrics const & fm =
307 theFontMetrics(font);
309 // There is a \thinspace on each side of the triangle
311 // ▹ U+25B9 WHITE RIGHT-POINTING SMALL TRIANGLE
312 // ◃ U+25C3 WHITE LEFT-POINTING SMALL TRIANGLE
313 char_type const c = pi.ltr_pos ? 0x25B9 : 0x25C3;
314 font.setColor(Color_special);
315 pi.pain.text(x, y, c, font);
320 font.setColor(Color_special);
321 pi.pain.text(x, y, char_type('/'), font);
326 font.setColor(Color_latex);
327 pi.pain.text(x, y, char_type('-'), font);
334 drawLogo(pi, x, y, kind_);
340 void InsetSpecialChar::write(ostream & os) const
345 command = "softhyphen";
348 command = "allowbreak";
351 command = "ligaturebreak";
353 case END_OF_SENTENCE:
354 command = "endofsentence";
360 command = "menuseparator";
363 command = "breakableslash";
366 command = "nobreakdash";
381 os << "\\SpecialChar " << command << "\n";
385 void InsetSpecialChar::read(Lexer & lex)
388 string const command = lex.getString();
390 if (command == "softhyphen")
392 else if (command == "allowbreak")
394 else if (command == "ligaturebreak")
395 kind_ = LIGATURE_BREAK;
396 else if (command == "endofsentence")
397 kind_ = END_OF_SENTENCE;
398 else if (command == "ldots")
400 else if (command == "menuseparator")
401 kind_ = MENU_SEPARATOR;
402 else if (command == "breakableslash")
404 else if (command == "nobreakdash")
406 else if (command == "LyX")
408 else if (command == "TeX")
410 else if (command == "LaTeX2e")
411 kind_ = PHRASE_LATEX2E;
412 else if (command == "LaTeX")
413 kind_ = PHRASE_LATEX;
415 lex.printError("InsetSpecialChar: Unknown kind: `$$Token'");
419 void InsetSpecialChar::latex(otexstream & os,
420 OutputParams const & rp) const
422 bool const rtl = rp.local_font->isRightToLeft();
424 string lswitche = "";
425 if (rtl && !rp.use_polyglossia) {
428 if (rp.local_font->language()->lang() == "arabic_arabi"
429 || rp.local_font->language()->lang() == "farsi")
430 lswitch = "\\textLR{";
438 os << "\\LyXZeroWidthSpace" << termcmd;
441 os << "\\textcompwordmark" << termcmd;
443 case END_OF_SENTENCE:
447 os << "\\ldots" << termcmd;
457 os << "\\slash" << termcmd;
462 os << "\\nobreakdash-";
467 os << lswitch << "\\LyX" << termcmd << lswitche;
472 os << lswitch << "\\TeX" << termcmd << lswitche;
477 os << lswitch << "\\LaTeXe" << termcmd << lswitche;
482 os << lswitch << "\\LaTeX" << termcmd << lswitche;
488 int InsetSpecialChar::plaintext(odocstringstream & os,
489 OutputParams const &, size_t) const
500 case END_OF_SENTENCE:
533 int InsetSpecialChar::docbook(odocstream & os, OutputParams const &) const
543 case END_OF_SENTENCE:
576 docstring InsetSpecialChar::xhtml(XHTMLStream & xs, OutputParams const &) const
582 xs << XHTMLStream::ESCAPE_NONE << "​";
585 xs << XHTMLStream::ESCAPE_NONE << "‌";
587 case END_OF_SENTENCE:
591 xs << XHTMLStream::ESCAPE_NONE << "…";
594 xs << XHTMLStream::ESCAPE_NONE << "⇒";
597 xs << XHTMLStream::ESCAPE_NONE << "⁄";
600 xs << XHTMLStream::ESCAPE_NONE << "‑";
609 xs << "LaTeX2" << XHTMLStream::ESCAPE_NONE << "ε";
619 void InsetSpecialChar::toString(odocstream & os) const
624 // Do not output ZERO WIDTH SPACE and ZERO WIDTH NON JOINER here
625 // Spell checker would choke on it.
630 odocstringstream ods;
631 plaintext(ods, OutputParams(0));
636 void InsetSpecialChar::forOutliner(docstring & os, size_t const,
639 odocstringstream ods;
640 plaintext(ods, OutputParams(0));
645 void InsetSpecialChar::validate(LaTeXFeatures & features) const
647 if (kind_ == ALLOWBREAK)
648 features.require("lyxzerowidthspace");
649 if (kind_ == MENU_SEPARATOR)
650 features.require("lyxarrow");
651 if (kind_ == NOBREAKDASH)
652 features.require("amsmath");
653 if (kind_ == PHRASE_LYX)
654 features.require("LyX");
658 bool InsetSpecialChar::isChar() const
660 return kind_ != HYPHENATION && kind_ != LIGATURE_BREAK;
664 bool InsetSpecialChar::isLetter() const
666 return kind_ == HYPHENATION || kind_ == LIGATURE_BREAK
667 || kind_ == NOBREAKDASH;
671 bool InsetSpecialChar::isLineSeparator() const
674 // this would be nice, but it does not work, since
675 // Paragraph::stripLeadingSpaces nukes the characters which
676 // have this property. I leave the code here, since it should
677 // eventually be made to work. (JMarc 20020327)
678 return kind_ == HYPHENATION || kind_ == ALLOWBREAK
679 || kind_ == MENU_SEPARATOR || kind_ == SLASH;