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"
19 #include "LaTeXFeatures.h"
21 #include "MetricsInfo.h"
22 #include "output_xhtml.h"
23 #include "texstream.h"
25 #include "frontends/FontMetrics.h"
26 #include "frontends/Painter.h"
28 #include "support/debug.h"
29 #include "support/docstream.h"
36 InsetSpecialChar::InsetSpecialChar(Kind k)
41 InsetSpecialChar::Kind InsetSpecialChar::kind() const
49 int logoWidth(FontInfo const & font, InsetSpecialChar::Kind kind) {
50 frontend::FontMetrics const & fm = theFontMetrics(font);
51 int const em = fm.em();
53 // See drawlogo() below to understand what this does.
55 case InsetSpecialChar::PHRASE_LYX:
56 width = fm.width(from_ascii("L")) - em / 6
57 + fm.width(from_ascii("Y")) - em / 8
58 + fm.width(from_ascii("X"));
61 case InsetSpecialChar::PHRASE_TEX:
62 width = fm.width(from_ascii("T")) - em / 6
63 + fm.width(from_ascii("E")) - em / 8
64 + fm.width(from_ascii("X"));
67 case InsetSpecialChar::PHRASE_LATEX2E:
68 width = logoWidth(font, InsetSpecialChar::PHRASE_LATEX)
70 + fm.width(from_ascii("2") + char_type(0x03b5));
72 case InsetSpecialChar::PHRASE_LATEX: {
73 FontInfo smaller = font;
74 smaller.decSize().decSize();
75 width = fm.width(from_ascii("L")) - 9 * em / 25
76 + theFontMetrics(smaller).width(from_ascii("A")) - 3 * em / 20
77 + logoWidth(font, InsetSpecialChar::PHRASE_TEX);
81 LYXERR0("No information for computing width of logo " << kind);
89 docstring InsetSpecialChar::toolTip(BufferView const &, int, int) const
94 message = from_ascii("Optional Line Break (ZWSP)");
97 message = from_ascii("Ligature Break (ZWNJ)");
100 message = from_ascii("End of Sentence");
103 message = from_ascii("Hyphenation Point");
106 message = from_ascii("Breakable Slash");
109 message = from_ascii("Protected Hyphen (SHY)");
118 void InsetSpecialChar::metrics(MetricsInfo & mi, Dimension & dim) const
120 frontend::FontMetrics const & fm =
121 theFontMetrics(mi.base.font);
122 dim.asc = fm.maxAscent();
123 dim.des = fm.maxDescent();
129 dim.wid = fm.em() / 8;
134 case END_OF_SENTENCE:
138 s = from_ascii(". . .");
141 s = from_ascii(" x ");
144 dim.wid = fm.width(from_ascii("-"));
146 dim.wid -= 2; // to make it look shorter
158 dim.wid = logoWidth(mi.base.font, kind_);
162 dim.wid = fm.width(s);
168 // helper function: draw text and update x.
169 void drawChar(PainterInfo & pi, int & x, int const y, char_type ch)
171 FontInfo font = pi.base.font;
172 font.setPaintColor(pi.textColor(font.realColor()));
173 pi.pain.text(x, y, ch, font);
174 x += theFontMetrics(font).width(ch);
178 void drawLogo(PainterInfo & pi, int & x, int const y, InsetSpecialChar::Kind kind)
180 FontInfo const & font = pi.base.font;
181 int const em = theFontMetrics(font).em();
183 case InsetSpecialChar::PHRASE_LYX:
185 * \providecommand{\LyX}{L\kern-.1667em\lower.25em\hbox{Y}\kern-.125emX\\@};
187 drawChar(pi, x, y, 'L');
189 drawChar(pi, x, y + em / 4, 'Y');
191 drawChar(pi, x, y, 'X');
194 case InsetSpecialChar::PHRASE_TEX: {
196 * \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@}
198 int const ex = theFontMetrics(font).ascent('x');
199 drawChar(pi, x, y, 'T');
201 drawChar(pi, x, y + ex / 2, 'E');
203 drawChar(pi, x, y, 'X');
206 case InsetSpecialChar::PHRASE_LATEX2E:
208 * \DeclareRobustCommand{\LaTeXe}{\mbox{\m@th
209 * \if b\expandafter\@car\f@series\@nil\boldmath\fi
210 * \LaTeX\kern.15em2$_{\textstyle\varepsilon}$}}
212 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_LATEX);
214 drawChar(pi, x, y, '2');
215 drawChar(pi, x, y + em / 4, char_type(0x03b5));
218 case InsetSpecialChar::PHRASE_LATEX: {
220 * \DeclareRobustCommand{\LaTeX}{L\kern-.36em%
222 * \vbox to\ht\z@{\hbox{\check@mathfonts
223 * \fontsize\sf@size\z@
224 * \math@fontsfalse\selectfont
231 drawChar(pi, x, y, 'L');
233 PainterInfo pi2 = pi;
234 pi2.base.font.decSize().decSize();
235 drawChar(pi2, x, y - em / 5, 'A');
237 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_TEX);
241 LYXERR0("No information for drawing logo " << kind);
247 void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const
249 FontInfo font = pi.base.font;
254 font.setColor(Color_special);
255 pi.pain.text(x, y, char_type('-'), font);
260 // A small vertical line
261 int const asc = theFontMetrics(pi.base.font).ascent('x');
262 int const desc = theFontMetrics(pi.base.font).descent('g');
263 int const x0 = x; // x + 1; // FIXME: incline,
264 int const x1 = x; // x - 1; // similar to LibreOffice?
265 int const y0 = y + desc;
266 int const y1 = y - asc / 3;
267 pi.pain.line(x0, y1, x1, y0, Color_special);
272 font.setColor(Color_special);
273 pi.pain.text(x, y, char_type('|'), font);
276 case END_OF_SENTENCE:
278 font.setColor(Color_special);
279 pi.pain.text(x, y, char_type('.'), font);
284 font.setColor(Color_special);
285 string ell = ". . . ";
286 docstring dell(ell.begin(), ell.end());
287 pi.pain.text(x, y, dell, font);
292 frontend::FontMetrics const & fm =
293 theFontMetrics(font);
295 // A triangle the width and height of an 'x'
296 int w = fm.width(char_type('x'));
297 int ox = fm.width(char_type(' ')) + x;
298 int h = fm.ascent(char_type('x'));
301 xp[0] = ox; yp[0] = y;
302 xp[1] = ox; yp[1] = y - h;
303 xp[2] = ox + w; yp[2] = y - h/2;
304 xp[3] = ox; yp[3] = y;
306 pi.pain.lines(xp, yp, 4, Color_special);
311 font.setColor(Color_special);
312 pi.pain.text(x, y, char_type('/'), font);
317 font.setColor(Color_latex);
318 pi.pain.text(x, y, char_type('-'), font);
325 drawLogo(pi, x, y, kind_);
331 void InsetSpecialChar::write(ostream & os) const
336 command = "softhyphen";
339 command = "allowbreak";
342 command = "ligaturebreak";
344 case END_OF_SENTENCE:
345 command = "endofsentence";
351 command = "menuseparator";
354 command = "breakableslash";
357 command = "nobreakdash";
372 os << "\\SpecialChar " << command << "\n";
376 void InsetSpecialChar::read(Lexer & lex)
379 string const command = lex.getString();
381 if (command == "softhyphen")
383 else if (command == "allowbreak")
385 else if (command == "ligaturebreak")
386 kind_ = LIGATURE_BREAK;
387 else if (command == "endofsentence")
388 kind_ = END_OF_SENTENCE;
389 else if (command == "ldots")
391 else if (command == "menuseparator")
392 kind_ = MENU_SEPARATOR;
393 else if (command == "breakableslash")
395 else if (command == "nobreakdash")
397 else if (command == "LyX")
399 else if (command == "TeX")
401 else if (command == "LaTeX2e")
402 kind_ = PHRASE_LATEX2E;
403 else if (command == "LaTeX")
404 kind_ = PHRASE_LATEX;
406 lex.printError("InsetSpecialChar: Unknown kind: `$$Token'");
410 void InsetSpecialChar::latex(otexstream & os,
411 OutputParams const & rp) const
418 os << "\\LyXZeroWidthSpace" << termcmd;
421 os << "\\textcompwordmark" << termcmd;
423 case END_OF_SENTENCE:
427 os << "\\ldots" << termcmd;
430 if (rp.local_font->isRightToLeft())
437 os << "\\slash" << termcmd;
442 os << "\\nobreakdash-";
447 os << "\\LyX" << termcmd;
452 os << "\\TeX" << termcmd;
457 os << "\\LaTeXe" << termcmd;
462 os << "\\LaTeX" << termcmd;
468 int InsetSpecialChar::plaintext(odocstringstream & os,
469 OutputParams const &, size_t) const
480 case END_OF_SENTENCE:
513 int InsetSpecialChar::docbook(odocstream & os, OutputParams const &) const
523 case END_OF_SENTENCE:
556 docstring InsetSpecialChar::xhtml(XHTMLStream & xs, OutputParams const &) const
562 xs << XHTMLStream::ESCAPE_NONE << "​";
565 xs << XHTMLStream::ESCAPE_NONE << "‌";
567 case END_OF_SENTENCE:
571 xs << XHTMLStream::ESCAPE_NONE << "…";
574 xs << XHTMLStream::ESCAPE_NONE << "⇒";
577 xs << XHTMLStream::ESCAPE_NONE << "⁄";
580 xs << XHTMLStream::ESCAPE_NONE << "‑";
589 xs << "LaTeX2" << XHTMLStream::ESCAPE_NONE << "ε";
599 void InsetSpecialChar::toString(odocstream & os) const
604 // Do not output ZERO WIDTH SPACE and ZERO WIDTH NON JOINER here
605 // Spell checker would choke on it.
610 odocstringstream ods;
611 plaintext(ods, OutputParams(0));
616 void InsetSpecialChar::forOutliner(docstring & os, size_t const,
619 odocstringstream ods;
620 plaintext(ods, OutputParams(0));
625 void InsetSpecialChar::validate(LaTeXFeatures & features) const
627 if (kind_ == ALLOWBREAK)
628 features.require("lyxzerowidthspace");
629 if (kind_ == MENU_SEPARATOR)
630 features.require("lyxarrow");
631 if (kind_ == NOBREAKDASH)
632 features.require("amsmath");
633 if (kind_ == PHRASE_LYX)
634 features.require("LyX");
638 bool InsetSpecialChar::isChar() const
640 return kind_ != HYPHENATION && kind_ != LIGATURE_BREAK;
644 bool InsetSpecialChar::isLetter() const
646 return kind_ == HYPHENATION || kind_ == LIGATURE_BREAK
647 || kind_ == NOBREAKDASH;
651 bool InsetSpecialChar::isLineSeparator() const
654 // this would be nice, but it does not work, since
655 // Paragraph::stripLeadingSpaces nukes the characters which
656 // have this property. I leave the code here, since it should
657 // eventually be made to work. (JMarc 20020327)
658 return kind_ == HYPHENATION || kind_ == ALLOWBREAK
659 || kind_ == MENU_SEPARATOR || kind_ == SLASH;