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)");
117 // no tooltip for these ones.
124 void InsetSpecialChar::metrics(MetricsInfo & mi, Dimension & dim) const
126 frontend::FontMetrics const & fm =
127 theFontMetrics(mi.base.font);
128 dim.asc = fm.maxAscent();
129 dim.des = fm.maxDescent();
135 dim.wid = fm.em() / 8;
140 case END_OF_SENTENCE:
144 s = from_ascii(". . .");
147 s = from_ascii(" x ");
150 dim.wid = fm.width(from_ascii("-"));
152 dim.wid -= 2; // to make it look shorter
164 dim.wid = logoWidth(mi.base.font, kind_);
168 dim.wid = fm.width(s);
174 // helper function: draw text and update x.
175 void drawChar(PainterInfo & pi, int & x, int const y, char_type ch)
177 FontInfo font = pi.base.font;
178 font.setPaintColor(pi.textColor(font.realColor()));
179 pi.pain.text(x, y, ch, font);
180 x += theFontMetrics(font).width(ch);
184 void drawLogo(PainterInfo & pi, int & x, int const y, InsetSpecialChar::Kind kind)
186 FontInfo const & font = pi.base.font;
187 int const em = theFontMetrics(font).em();
189 case InsetSpecialChar::PHRASE_LYX:
191 * \providecommand{\LyX}{L\kern-.1667em\lower.25em\hbox{Y}\kern-.125emX\\@};
193 drawChar(pi, x, y, 'L');
195 drawChar(pi, x, y + em / 4, 'Y');
197 drawChar(pi, x, y, 'X');
200 case InsetSpecialChar::PHRASE_TEX: {
202 * \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@}
204 int const ex = theFontMetrics(font).ascent('x');
205 drawChar(pi, x, y, 'T');
207 drawChar(pi, x, y + ex / 2, 'E');
209 drawChar(pi, x, y, 'X');
212 case InsetSpecialChar::PHRASE_LATEX2E:
214 * \DeclareRobustCommand{\LaTeXe}{\mbox{\m@th
215 * \if b\expandafter\@car\f@series\@nil\boldmath\fi
216 * \LaTeX\kern.15em2$_{\textstyle\varepsilon}$}}
218 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_LATEX);
220 drawChar(pi, x, y, '2');
221 drawChar(pi, x, y + em / 4, char_type(0x03b5));
224 case InsetSpecialChar::PHRASE_LATEX: {
226 * \DeclareRobustCommand{\LaTeX}{L\kern-.36em%
228 * \vbox to\ht\z@{\hbox{\check@mathfonts
229 * \fontsize\sf@size\z@
230 * \math@fontsfalse\selectfont
237 drawChar(pi, x, y, 'L');
239 PainterInfo pi2 = pi;
240 pi2.base.font.decSize().decSize();
241 drawChar(pi2, x, y - em / 5, 'A');
243 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_TEX);
247 LYXERR0("No information for drawing logo " << kind);
253 void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const
255 FontInfo font = pi.base.font;
260 font.setColor(Color_special);
261 pi.pain.text(x, y, char_type('-'), font);
266 // A small vertical line
267 int const asc = theFontMetrics(pi.base.font).ascent('x');
268 int const desc = theFontMetrics(pi.base.font).descent('g');
269 int const x0 = x; // x + 1; // FIXME: incline,
270 int const x1 = x; // x - 1; // similar to LibreOffice?
271 int const y0 = y + desc;
272 int const y1 = y - asc / 3;
273 pi.pain.line(x0, y1, x1, y0, Color_special);
278 font.setColor(Color_special);
279 pi.pain.text(x, y, char_type('|'), font);
282 case END_OF_SENTENCE:
284 font.setColor(Color_special);
285 pi.pain.text(x, y, char_type('.'), font);
290 font.setColor(Color_special);
291 string ell = ". . . ";
292 docstring dell(ell.begin(), ell.end());
293 pi.pain.text(x, y, dell, font);
298 frontend::FontMetrics const & fm =
299 theFontMetrics(font);
301 // A triangle the width and height of an 'x'
302 int w = fm.width(char_type('x'));
303 int ox = fm.width(char_type(' ')) + x;
304 int h = fm.ascent(char_type('x'));
307 xp[0] = ox; yp[0] = y;
308 xp[1] = ox; yp[1] = y - h;
309 xp[2] = ox + w; yp[2] = y - h/2;
310 xp[3] = ox; yp[3] = y;
312 pi.pain.lines(xp, yp, 4, Color_special);
317 font.setColor(Color_special);
318 pi.pain.text(x, y, char_type('/'), font);
323 font.setColor(Color_latex);
324 pi.pain.text(x, y, char_type('-'), font);
331 drawLogo(pi, x, y, kind_);
337 void InsetSpecialChar::write(ostream & os) const
342 command = "softhyphen";
345 command = "allowbreak";
348 command = "ligaturebreak";
350 case END_OF_SENTENCE:
351 command = "endofsentence";
357 command = "menuseparator";
360 command = "breakableslash";
363 command = "nobreakdash";
378 os << "\\SpecialChar " << command << "\n";
382 void InsetSpecialChar::read(Lexer & lex)
385 string const command = lex.getString();
387 if (command == "softhyphen")
389 else if (command == "allowbreak")
391 else if (command == "ligaturebreak")
392 kind_ = LIGATURE_BREAK;
393 else if (command == "endofsentence")
394 kind_ = END_OF_SENTENCE;
395 else if (command == "ldots")
397 else if (command == "menuseparator")
398 kind_ = MENU_SEPARATOR;
399 else if (command == "breakableslash")
401 else if (command == "nobreakdash")
403 else if (command == "LyX")
405 else if (command == "TeX")
407 else if (command == "LaTeX2e")
408 kind_ = PHRASE_LATEX2E;
409 else if (command == "LaTeX")
410 kind_ = PHRASE_LATEX;
412 lex.printError("InsetSpecialChar: Unknown kind: `$$Token'");
416 void InsetSpecialChar::latex(otexstream & os,
417 OutputParams const & rp) const
424 os << "\\LyXZeroWidthSpace" << termcmd;
427 os << "\\textcompwordmark" << termcmd;
429 case END_OF_SENTENCE:
433 os << "\\ldots" << termcmd;
436 if (rp.local_font->isRightToLeft())
443 os << "\\slash" << termcmd;
448 os << "\\nobreakdash-";
453 os << "\\LyX" << termcmd;
458 os << "\\TeX" << termcmd;
463 os << "\\LaTeXe" << termcmd;
468 os << "\\LaTeX" << termcmd;
474 int InsetSpecialChar::plaintext(odocstringstream & os,
475 OutputParams const &, size_t) const
486 case END_OF_SENTENCE:
519 int InsetSpecialChar::docbook(odocstream & os, OutputParams const &) const
529 case END_OF_SENTENCE:
562 docstring InsetSpecialChar::xhtml(XHTMLStream & xs, OutputParams const &) const
568 xs << XHTMLStream::ESCAPE_NONE << "​";
571 xs << XHTMLStream::ESCAPE_NONE << "‌";
573 case END_OF_SENTENCE:
577 xs << XHTMLStream::ESCAPE_NONE << "…";
580 xs << XHTMLStream::ESCAPE_NONE << "⇒";
583 xs << XHTMLStream::ESCAPE_NONE << "⁄";
586 xs << XHTMLStream::ESCAPE_NONE << "‑";
595 xs << "LaTeX2" << XHTMLStream::ESCAPE_NONE << "ε";
605 void InsetSpecialChar::toString(odocstream & os) const
610 // Do not output ZERO WIDTH SPACE and ZERO WIDTH NON JOINER here
611 // Spell checker would choke on it.
616 odocstringstream ods;
617 plaintext(ods, OutputParams(0));
622 void InsetSpecialChar::forOutliner(docstring & os, size_t const,
625 odocstringstream ods;
626 plaintext(ods, OutputParams(0));
631 void InsetSpecialChar::validate(LaTeXFeatures & features) const
633 if (kind_ == ALLOWBREAK)
634 features.require("lyxzerowidthspace");
635 if (kind_ == MENU_SEPARATOR)
636 features.require("lyxarrow");
637 if (kind_ == NOBREAKDASH)
638 features.require("amsmath");
639 if (kind_ == PHRASE_LYX)
640 features.require("LyX");
644 bool InsetSpecialChar::isChar() const
646 return kind_ != HYPHENATION && kind_ != LIGATURE_BREAK;
650 bool InsetSpecialChar::isLetter() const
652 return kind_ == HYPHENATION || kind_ == LIGATURE_BREAK
653 || kind_ == NOBREAKDASH;
657 bool InsetSpecialChar::isLineSeparator() const
660 // this would be nice, but it does not work, since
661 // Paragraph::stripLeadingSpaces nukes the characters which
662 // have this property. I leave the code here, since it should
663 // eventually be made to work. (JMarc 20020327)
664 return kind_ == HYPHENATION || kind_ == ALLOWBREAK
665 || kind_ == MENU_SEPARATOR || kind_ == SLASH;