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();
130 dim.des = fm.maxDescent();
136 dim.wid = fm.em() / 8;
141 case END_OF_SENTENCE:
145 s = from_ascii(". . .");
148 s = from_ascii(" x ");
151 dim.wid = fm.width(from_ascii("-"));
153 dim.wid -= 2; // to make it look shorter
165 dim.wid = logoWidth(mi.base.font, kind_);
169 dim.wid = fm.width(s);
175 // helper function: draw text and update x.
176 void drawChar(PainterInfo & pi, int & x, int const y, char_type ch)
178 FontInfo font = pi.base.font;
179 font.setPaintColor(pi.textColor(font.realColor()));
180 pi.pain.text(x, y, ch, font);
181 x += theFontMetrics(font).width(ch);
185 void drawLogo(PainterInfo & pi, int & x, int const y, InsetSpecialChar::Kind kind)
187 FontInfo const & font = pi.base.font;
188 int const em = theFontMetrics(font).em();
190 case InsetSpecialChar::PHRASE_LYX:
192 * \providecommand{\LyX}{L\kern-.1667em\lower.25em\hbox{Y}\kern-.125emX\\@};
194 drawChar(pi, x, y, 'L');
196 drawChar(pi, x, y + em / 4, 'Y');
198 drawChar(pi, x, y, 'X');
201 case InsetSpecialChar::PHRASE_TEX: {
203 * \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@}
205 int const ex = theFontMetrics(font).ascent('x');
206 drawChar(pi, x, y, 'T');
208 drawChar(pi, x, y + ex / 2, 'E');
210 drawChar(pi, x, y, 'X');
213 case InsetSpecialChar::PHRASE_LATEX2E:
215 * \DeclareRobustCommand{\LaTeXe}{\mbox{\m@th
216 * \if b\expandafter\@car\f@series\@nil\boldmath\fi
217 * \LaTeX\kern.15em2$_{\textstyle\varepsilon}$}}
219 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_LATEX);
221 drawChar(pi, x, y, '2');
222 drawChar(pi, x, y + em / 4, char_type(0x03b5));
225 case InsetSpecialChar::PHRASE_LATEX: {
227 * \DeclareRobustCommand{\LaTeX}{L\kern-.36em%
229 * \vbox to\ht\z@{\hbox{\check@mathfonts
230 * \fontsize\sf@size\z@
231 * \math@fontsfalse\selectfont
238 drawChar(pi, x, y, 'L');
240 PainterInfo pi2 = pi;
241 pi2.base.font.decSize().decSize();
242 drawChar(pi2, x, y - em / 5, 'A');
244 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_TEX);
248 LYXERR0("No information for drawing logo " << kind);
254 void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const
256 FontInfo font = pi.base.font;
261 font.setColor(Color_special);
262 pi.pain.text(x, y, char_type('-'), font);
267 // A small vertical line
268 int const asc = theFontMetrics(pi.base.font).ascent('x');
269 int const desc = theFontMetrics(pi.base.font).descent('g');
270 int const x0 = x; // x + 1; // FIXME: incline,
271 int const x1 = x; // x - 1; // similar to LibreOffice?
272 int const y0 = y + desc;
273 int const y1 = y - asc / 3;
274 pi.pain.line(x0, y1, x1, y0, Color_special);
279 font.setColor(Color_special);
280 pi.pain.text(x, y, char_type('|'), font);
283 case END_OF_SENTENCE:
285 font.setColor(Color_special);
286 pi.pain.text(x, y, char_type('.'), font);
291 font.setColor(Color_special);
292 string ell = ". . . ";
293 docstring dell(ell.begin(), ell.end());
294 pi.pain.text(x, y, dell, font);
299 frontend::FontMetrics const & fm =
300 theFontMetrics(font);
302 // A triangle the width and height of an 'x'
303 int w = fm.width(char_type('x'));
304 int ox = fm.width(char_type(' ')) + x;
305 int h = fm.ascent(char_type('x'));
308 xp[0] = ox; yp[0] = y;
309 xp[1] = ox; yp[1] = y - h;
310 xp[2] = ox + w; yp[2] = y - h/2;
311 xp[3] = ox; yp[3] = y;
313 pi.pain.lines(xp, yp, 4, Color_special);
318 font.setColor(Color_special);
319 pi.pain.text(x, y, char_type('/'), font);
324 font.setColor(Color_latex);
325 pi.pain.text(x, y, char_type('-'), font);
332 drawLogo(pi, x, y, kind_);
338 void InsetSpecialChar::write(ostream & os) const
343 command = "softhyphen";
346 command = "allowbreak";
349 command = "ligaturebreak";
351 case END_OF_SENTENCE:
352 command = "endofsentence";
358 command = "menuseparator";
361 command = "breakableslash";
364 command = "nobreakdash";
379 os << "\\SpecialChar " << command << "\n";
383 void InsetSpecialChar::read(Lexer & lex)
386 string const command = lex.getString();
388 if (command == "softhyphen")
390 else if (command == "allowbreak")
392 else if (command == "ligaturebreak")
393 kind_ = LIGATURE_BREAK;
394 else if (command == "endofsentence")
395 kind_ = END_OF_SENTENCE;
396 else if (command == "ldots")
398 else if (command == "menuseparator")
399 kind_ = MENU_SEPARATOR;
400 else if (command == "breakableslash")
402 else if (command == "nobreakdash")
404 else if (command == "LyX")
406 else if (command == "TeX")
408 else if (command == "LaTeX2e")
409 kind_ = PHRASE_LATEX2E;
410 else if (command == "LaTeX")
411 kind_ = PHRASE_LATEX;
413 lex.printError("InsetSpecialChar: Unknown kind: `$$Token'");
417 void InsetSpecialChar::latex(otexstream & os,
418 OutputParams const & rp) const
420 bool const rtl = rp.local_font->isRightToLeft();
422 string lswitche = "";
423 if (rtl && !rp.use_polyglossia) {
426 if (rp.local_font->language()->lang() == "arabic_arabi"
427 || rp.local_font->language()->lang() == "farsi")
428 lswitch = "\\textLR{";
436 os << "\\LyXZeroWidthSpace" << termcmd;
439 os << "\\textcompwordmark" << termcmd;
441 case END_OF_SENTENCE:
445 os << "\\ldots" << termcmd;
455 os << "\\slash" << termcmd;
460 os << "\\nobreakdash-";
465 os << lswitch << "\\LyX" << termcmd << lswitche;
470 os << lswitch << "\\TeX" << termcmd << lswitche;
475 os << lswitch << "\\LaTeXe" << termcmd << lswitche;
480 os << lswitch << "\\LaTeX" << termcmd << lswitche;
486 int InsetSpecialChar::plaintext(odocstringstream & os,
487 OutputParams const &, size_t) const
498 case END_OF_SENTENCE:
531 int InsetSpecialChar::docbook(odocstream & os, OutputParams const &) const
541 case END_OF_SENTENCE:
574 docstring InsetSpecialChar::xhtml(XHTMLStream & xs, OutputParams const &) const
580 xs << XHTMLStream::ESCAPE_NONE << "​";
583 xs << XHTMLStream::ESCAPE_NONE << "‌";
585 case END_OF_SENTENCE:
589 xs << XHTMLStream::ESCAPE_NONE << "…";
592 xs << XHTMLStream::ESCAPE_NONE << "⇒";
595 xs << XHTMLStream::ESCAPE_NONE << "⁄";
598 xs << XHTMLStream::ESCAPE_NONE << "‑";
607 xs << "LaTeX2" << XHTMLStream::ESCAPE_NONE << "ε";
617 void InsetSpecialChar::toString(odocstream & os) const
622 // Do not output ZERO WIDTH SPACE and ZERO WIDTH NON JOINER here
623 // Spell checker would choke on it.
628 odocstringstream ods;
629 plaintext(ods, OutputParams(0));
634 void InsetSpecialChar::forOutliner(docstring & os, size_t const,
637 odocstringstream ods;
638 plaintext(ods, OutputParams(0));
643 void InsetSpecialChar::validate(LaTeXFeatures & features) const
645 if (kind_ == ALLOWBREAK)
646 features.require("lyxzerowidthspace");
647 if (kind_ == MENU_SEPARATOR)
648 features.require("lyxarrow");
649 if (kind_ == NOBREAKDASH)
650 features.require("amsmath");
651 if (kind_ == PHRASE_LYX)
652 features.require("LyX");
656 bool InsetSpecialChar::isChar() const
658 return kind_ != HYPHENATION && kind_ != LIGATURE_BREAK;
662 bool InsetSpecialChar::isLetter() const
664 return kind_ == HYPHENATION || kind_ == LIGATURE_BREAK
665 || kind_ == NOBREAKDASH;
669 bool InsetSpecialChar::isLineSeparator() const
672 // this would be nice, but it does not work, since
673 // Paragraph::stripLeadingSpaces nukes the characters which
674 // have this property. I leave the code here, since it should
675 // eventually be made to work. (JMarc 20020327)
676 return kind_ == HYPHENATION || kind_ == ALLOWBREAK
677 || kind_ == MENU_SEPARATOR || kind_ == SLASH;