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/NullPainter.h"
28 #include "frontends/Painter.h"
30 #include "support/debug.h"
31 #include "support/docstream.h"
38 InsetSpecialChar::InsetSpecialChar(Kind k)
43 InsetSpecialChar::Kind InsetSpecialChar::kind() const
49 docstring InsetSpecialChar::toolTip(BufferView const &, int, int) const
54 message = from_ascii("Optional Line Break (ZWSP)");
57 message = from_ascii("Ligature Break (ZWNJ)");
60 message = from_ascii("End of Sentence");
63 message = from_ascii("Hyphenation Point");
66 message = from_ascii("Breakable Slash");
69 message = from_ascii("Protected Hyphen (SHY)");
77 // no tooltip for these ones.
86 // helper function: draw text and update x.
87 void drawChar(PainterInfo & pi, int & x, int const y, char_type ch)
89 FontInfo font = pi.base.font;
90 font.setPaintColor(pi.textColor(font.realColor()));
91 pi.pain.text(x, y, ch, font);
92 x += theFontMetrics(font).width(ch);
96 void drawLogo(PainterInfo & pi, int & x, int const y, InsetSpecialChar::Kind kind)
98 FontInfo const & font = pi.base.font;
99 int const em = theFontMetrics(font).em();
101 case InsetSpecialChar::PHRASE_LYX:
103 * \providecommand{\LyX}{L\kern-.1667em\lower.25em\hbox{Y}\kern-.125emX\\@};
105 drawChar(pi, x, y, 'L');
107 drawChar(pi, x, y + em / 4, 'Y');
109 drawChar(pi, x, y, 'X');
112 case InsetSpecialChar::PHRASE_TEX: {
114 * \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@}
116 int const ex = theFontMetrics(font).xHeight();
117 drawChar(pi, x, y, 'T');
119 drawChar(pi, x, y + ex / 2, 'E');
121 drawChar(pi, x, y, 'X');
124 case InsetSpecialChar::PHRASE_LATEX2E:
126 * \DeclareRobustCommand{\LaTeXe}{\mbox{\m@th
127 * \if b\expandafter\@car\f@series\@nil\boldmath\fi
128 * \LaTeX\kern.15em2$_{\textstyle\varepsilon}$}}
130 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_LATEX);
132 drawChar(pi, x, y, '2');
133 // ε U+03B5 GREEK SMALL LETTER EPSILON
134 drawChar(pi, x, y + em / 4, char_type(0x03b5));
137 case InsetSpecialChar::PHRASE_LATEX: {
139 * \DeclareRobustCommand{\LaTeX}{L\kern-.36em%
141 * \vbox to\ht\z@{\hbox{\check@mathfonts
142 * \fontsize\sf@size\z@
143 * \math@fontsfalse\selectfont
150 drawChar(pi, x, y, 'L');
152 PainterInfo pi2 = pi;
153 pi2.base.font.decSize().decSize();
154 drawChar(pi2, x, y - em / 5, 'A');
156 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_TEX);
160 LYXERR0("No information for drawing logo " << kind);
167 void InsetSpecialChar::metrics(MetricsInfo & mi, Dimension & dim) const
169 frontend::FontMetrics const & fm =
170 theFontMetrics(mi.base.font);
171 dim.asc = fm.maxAscent();
178 dim.asc = fm.xHeight();
179 dim.des = fm.descent('g');
180 dim.wid = fm.em() / 8;
185 case END_OF_SENTENCE:
189 s = from_ascii(". . .");
192 // ▹ U+25B9 WHITE RIGHT-POINTING SMALL TRIANGLE
193 // There is a \thinspace on each side of the triangle
194 dim.wid = 2 * fm.em() / 6 + fm.width(char_type(0x25B9));
197 dim.wid = fm.width(from_ascii("-"));
199 dim.wid -= 2; // to make it look shorter
203 dim.des = fm.descent(s[0]);
212 dim.asc = fm.maxAscent();
213 dim.des = fm.maxDescent();
214 frontend::NullPainter np;
215 PainterInfo pi(mi.base.bv, np);
216 pi.base.font = mi.base.font;
217 drawLogo(pi, dim.wid, 0, kind_);
221 dim.wid = fm.width(s);
225 void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const
227 FontInfo font = pi.base.font;
232 font.setColor(Color_special);
233 pi.pain.text(x, y, char_type('-'), font);
238 // A small vertical line
239 int const asc = theFontMetrics(pi.base.font).xHeight();
240 int const desc = theFontMetrics(pi.base.font).descent('g');
241 int const x0 = x; // x + 1; // FIXME: incline,
242 int const x1 = x; // x - 1; // similar to LibreOffice?
243 int const y0 = y + desc;
244 int const y1 = y - asc / 3;
245 pi.pain.line(x0, y1, x1, y0, Color_special);
250 font.setColor(Color_special);
251 pi.pain.text(x, y, char_type('|'), font);
254 case END_OF_SENTENCE:
256 font.setColor(Color_special);
257 pi.pain.text(x, y, char_type('.'), font);
262 font.setColor(Color_special);
263 string ell = ". . . ";
264 docstring dell(ell.begin(), ell.end());
265 pi.pain.text(x, y, dell, font);
270 frontend::FontMetrics const & fm =
271 theFontMetrics(font);
273 // There is a \thinspace on each side of the triangle
275 // ▹ U+25B9 WHITE RIGHT-POINTING SMALL TRIANGLE
276 // ◃ U+25C3 WHITE LEFT-POINTING SMALL TRIANGLE
277 char_type const c = pi.ltr_pos ? 0x25B9 : 0x25C3;
278 font.setColor(Color_special);
279 pi.pain.text(x, y, c, font);
284 font.setColor(Color_special);
285 pi.pain.text(x, y, char_type('/'), font);
290 font.setColor(Color_latex);
291 pi.pain.text(x, y, char_type('-'), font);
298 drawLogo(pi, x, y, kind_);
304 void InsetSpecialChar::write(ostream & os) const
309 command = "softhyphen";
312 command = "allowbreak";
315 command = "ligaturebreak";
317 case END_OF_SENTENCE:
318 command = "endofsentence";
324 command = "menuseparator";
327 command = "breakableslash";
330 command = "nobreakdash";
345 os << "\\SpecialChar " << command << "\n";
349 void InsetSpecialChar::read(Lexer & lex)
352 string const command = lex.getString();
354 if (command == "softhyphen")
356 else if (command == "allowbreak")
358 else if (command == "ligaturebreak")
359 kind_ = LIGATURE_BREAK;
360 else if (command == "endofsentence")
361 kind_ = END_OF_SENTENCE;
362 else if (command == "ldots")
364 else if (command == "menuseparator")
365 kind_ = MENU_SEPARATOR;
366 else if (command == "breakableslash")
368 else if (command == "nobreakdash")
370 else if (command == "LyX")
372 else if (command == "TeX")
374 else if (command == "LaTeX2e")
375 kind_ = PHRASE_LATEX2E;
376 else if (command == "LaTeX")
377 kind_ = PHRASE_LATEX;
379 lex.printError("InsetSpecialChar: Unknown kind: `$$Token'");
383 void InsetSpecialChar::latex(otexstream & os,
384 OutputParams const & rp) const
386 bool const rtl = rp.local_font->isRightToLeft();
388 string lswitche = "";
389 if (rtl && !rp.use_polyglossia) {
392 if (rp.local_font->language()->lang() == "arabic_arabi"
393 || rp.local_font->language()->lang() == "farsi")
394 lswitch = "\\textLR{";
402 os << "\\LyXZeroWidthSpace" << termcmd;
405 os << "\\textcompwordmark" << termcmd;
407 case END_OF_SENTENCE:
411 os << "\\ldots" << termcmd;
421 os << "\\slash" << termcmd;
426 os << "\\nobreakdash-";
431 os << lswitch << "\\LyX" << termcmd << lswitche;
436 os << lswitch << "\\TeX" << termcmd << lswitche;
441 os << lswitch << "\\LaTeXe" << termcmd << lswitche;
446 os << lswitch << "\\LaTeX" << termcmd << lswitche;
452 int InsetSpecialChar::plaintext(odocstringstream & os,
453 OutputParams const &, size_t) const
459 // U+200B ZERO WIDTH SPACE (ZWSP)
463 // U+200C ZERO WIDTH NON-JOINER
466 case END_OF_SENTENCE:
470 // … U+2026 HORIZONTAL ELLIPSIS
480 // ‑ U+2011 NON-BREAKING HYPHEN
491 // ε U+03B5 GREEK SMALL LETTER EPSILON
502 int InsetSpecialChar::docbook(odocstream & os, OutputParams const &) const
508 // U+200B ZERO WIDTH SPACE (ZWSP)
513 case END_OF_SENTENCE:
536 // ε U+03B5 GREEK SMALL LETTER EPSILON
547 docstring InsetSpecialChar::xhtml(XHTMLStream & xs, OutputParams const &) const
553 xs << XHTMLStream::ESCAPE_NONE << "​";
556 xs << XHTMLStream::ESCAPE_NONE << "‌";
558 case END_OF_SENTENCE:
562 xs << XHTMLStream::ESCAPE_NONE << "…";
565 xs << XHTMLStream::ESCAPE_NONE << "⇒";
568 xs << XHTMLStream::ESCAPE_NONE << "⁄";
571 xs << XHTMLStream::ESCAPE_NONE << "‑";
580 xs << "LaTeX2" << XHTMLStream::ESCAPE_NONE << "ε";
590 void InsetSpecialChar::toString(odocstream & os) const
595 // Do not output ZERO WIDTH SPACE and ZERO WIDTH NON JOINER here
596 // Spell checker would choke on it.
601 odocstringstream ods;
602 plaintext(ods, OutputParams(0));
607 void InsetSpecialChar::forOutliner(docstring & os, size_t const,
610 odocstringstream ods;
611 plaintext(ods, OutputParams(0));
616 void InsetSpecialChar::validate(LaTeXFeatures & features) const
618 if (kind_ == ALLOWBREAK)
619 features.require("lyxzerowidthspace");
620 if (kind_ == MENU_SEPARATOR)
621 features.require("lyxarrow");
622 if (kind_ == NOBREAKDASH)
623 features.require("amsmath");
624 if (kind_ == PHRASE_LYX)
625 features.require("LyX");
629 bool InsetSpecialChar::isChar() const
631 return kind_ != HYPHENATION && kind_ != LIGATURE_BREAK;
635 bool InsetSpecialChar::isLetter() const
637 return kind_ == HYPHENATION || kind_ == LIGATURE_BREAK
638 || kind_ == NOBREAKDASH;
642 bool InsetSpecialChar::isLineSeparator() const
645 // this would be nice, but it does not work, since
646 // Paragraph::stripLeadingSpaces nukes the characters which
647 // have this property. I leave the code here, since it should
648 // eventually be made to work. (JMarc 20020327)
649 return kind_ == HYPHENATION || kind_ == ALLOWBREAK
650 || kind_ == MENU_SEPARATOR || kind_ == SLASH;