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)");
116 void InsetSpecialChar::metrics(MetricsInfo & mi, Dimension & dim) const
118 frontend::FontMetrics const & fm =
119 theFontMetrics(mi.base.font);
120 dim.asc = fm.maxAscent();
121 dim.des = fm.maxDescent();
127 dim.wid = fm.em() / 8;
132 case END_OF_SENTENCE:
136 s = from_ascii(". . .");
139 s = from_ascii(" x ");
142 dim.wid = fm.width(from_ascii("-"));
144 dim.wid -= 2; // to make it look shorter
156 dim.wid = logoWidth(mi.base.font, kind_);
160 dim.wid = fm.width(s);
166 // helper function: draw text and update x.
167 void drawChar(PainterInfo & pi, int & x, int const y, char_type ch)
169 FontInfo font = pi.base.font;
170 font.setPaintColor(pi.textColor(font.realColor()));
171 pi.pain.text(x, y, ch, font);
172 x += theFontMetrics(font).width(ch);
176 void drawLogo(PainterInfo & pi, int & x, int const y, InsetSpecialChar::Kind kind)
178 FontInfo const & font = pi.base.font;
179 int const em = theFontMetrics(font).em();
181 case InsetSpecialChar::PHRASE_LYX:
183 * \providecommand{\LyX}{L\kern-.1667em\lower.25em\hbox{Y}\kern-.125emX\\@};
185 drawChar(pi, x, y, 'L');
187 drawChar(pi, x, y + em / 4, 'Y');
189 drawChar(pi, x, y, 'X');
192 case InsetSpecialChar::PHRASE_TEX: {
194 * \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@}
196 int const ex = theFontMetrics(font).ascent('x');
197 drawChar(pi, x, y, 'T');
199 drawChar(pi, x, y + ex / 2, 'E');
201 drawChar(pi, x, y, 'X');
204 case InsetSpecialChar::PHRASE_LATEX2E:
206 * \DeclareRobustCommand{\LaTeXe}{\mbox{\m@th
207 * \if b\expandafter\@car\f@series\@nil\boldmath\fi
208 * \LaTeX\kern.15em2$_{\textstyle\varepsilon}$}}
210 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_LATEX);
212 drawChar(pi, x, y, '2');
213 drawChar(pi, x, y + em / 4, char_type(0x03b5));
216 case InsetSpecialChar::PHRASE_LATEX: {
218 * \DeclareRobustCommand{\LaTeX}{L\kern-.36em%
220 * \vbox to\ht\z@{\hbox{\check@mathfonts
221 * \fontsize\sf@size\z@
222 * \math@fontsfalse\selectfont
229 drawChar(pi, x, y, 'L');
231 PainterInfo pi2 = pi;
232 pi2.base.font.decSize().decSize();
233 drawChar(pi2, x, y - em / 5, 'A');
235 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_TEX);
239 LYXERR0("No information for drawing logo " << kind);
245 void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const
247 FontInfo font = pi.base.font;
252 font.setColor(Color_special);
253 pi.pain.text(x, y, char_type('-'), font);
258 // A small vertical line
259 int const asc = theFontMetrics(pi.base.font).ascent('x');
260 int const desc = theFontMetrics(pi.base.font).descent('g');
261 int const x0 = x; // x + 1; // FIXME: incline,
262 int const x1 = x; // x - 1; // similar to LibreOffice?
263 int const y0 = y + desc;
264 int const y1 = y - asc / 3;
265 pi.pain.line(x0, y1, x1, y0, Color_special);
270 font.setColor(Color_special);
271 pi.pain.text(x, y, char_type('|'), font);
274 case END_OF_SENTENCE:
276 font.setColor(Color_special);
277 pi.pain.text(x, y, char_type('.'), font);
282 font.setColor(Color_special);
283 string ell = ". . . ";
284 docstring dell(ell.begin(), ell.end());
285 pi.pain.text(x, y, dell, font);
290 frontend::FontMetrics const & fm =
291 theFontMetrics(font);
293 // A triangle the width and height of an 'x'
294 int w = fm.width(char_type('x'));
295 int ox = fm.width(char_type(' ')) + x;
296 int h = fm.ascent(char_type('x'));
299 xp[0] = ox; yp[0] = y;
300 xp[1] = ox; yp[1] = y - h;
301 xp[2] = ox + w; yp[2] = y - h/2;
302 xp[3] = ox; yp[3] = y;
304 pi.pain.lines(xp, yp, 4, Color_special);
309 font.setColor(Color_special);
310 pi.pain.text(x, y, char_type('/'), font);
315 font.setColor(Color_latex);
316 pi.pain.text(x, y, char_type('-'), font);
323 drawLogo(pi, x, y, kind_);
329 void InsetSpecialChar::write(ostream & os) const
334 command = "softhyphen";
337 command = "allowbreak";
340 command = "ligaturebreak";
342 case END_OF_SENTENCE:
343 command = "endofsentence";
349 command = "menuseparator";
352 command = "breakableslash";
355 command = "nobreakdash";
370 os << "\\SpecialChar " << command << "\n";
374 void InsetSpecialChar::read(Lexer & lex)
377 string const command = lex.getString();
379 if (command == "softhyphen")
381 else if (command == "allowbreak")
383 else if (command == "ligaturebreak")
384 kind_ = LIGATURE_BREAK;
385 else if (command == "endofsentence")
386 kind_ = END_OF_SENTENCE;
387 else if (command == "ldots")
389 else if (command == "menuseparator")
390 kind_ = MENU_SEPARATOR;
391 else if (command == "breakableslash")
393 else if (command == "nobreakdash")
395 else if (command == "LyX")
397 else if (command == "TeX")
399 else if (command == "LaTeX2e")
400 kind_ = PHRASE_LATEX2E;
401 else if (command == "LaTeX")
402 kind_ = PHRASE_LATEX;
404 lex.printError("InsetSpecialChar: Unknown kind: `$$Token'");
408 void InsetSpecialChar::latex(otexstream & os,
409 OutputParams const & rp) const
416 os << "\\LyXZeroWidthSpace" << termcmd;
419 os << "\\textcompwordmark" << termcmd;
421 case END_OF_SENTENCE:
425 os << "\\ldots" << termcmd;
428 if (rp.local_font->isRightToLeft())
435 os << "\\slash" << termcmd;
440 os << "\\nobreakdash-";
445 os << "\\LyX" << termcmd;
450 os << "\\TeX" << termcmd;
455 os << "\\LaTeXe" << termcmd;
460 os << "\\LaTeX" << termcmd;
466 int InsetSpecialChar::plaintext(odocstringstream & os,
467 OutputParams const &, size_t) const
478 case END_OF_SENTENCE:
511 int InsetSpecialChar::docbook(odocstream & os, OutputParams const &) const
521 case END_OF_SENTENCE:
554 docstring InsetSpecialChar::xhtml(XHTMLStream & xs, OutputParams const &) const
560 xs << XHTMLStream::ESCAPE_NONE << "​";
563 xs << XHTMLStream::ESCAPE_NONE << "‌";
565 case END_OF_SENTENCE:
569 xs << XHTMLStream::ESCAPE_NONE << "…";
572 xs << XHTMLStream::ESCAPE_NONE << "⇒";
575 xs << XHTMLStream::ESCAPE_NONE << "⁄";
578 xs << XHTMLStream::ESCAPE_NONE << "‑";
587 xs << "LaTeX2" << XHTMLStream::ESCAPE_NONE << "ε";
597 void InsetSpecialChar::toString(odocstream & os) const
602 // Do not output ZERO WIDTH SPACE and ZERO WIDTH NON JOINER here
603 // Spell checker would choke on it.
608 odocstringstream ods;
609 plaintext(ods, OutputParams(0));
614 void InsetSpecialChar::forOutliner(docstring & os, size_t const,
617 odocstringstream ods;
618 plaintext(ods, OutputParams(0));
623 void InsetSpecialChar::validate(LaTeXFeatures & features) const
625 if (kind_ == ALLOWBREAK)
626 features.require("lyxzerowidthspace");
627 if (kind_ == MENU_SEPARATOR)
628 features.require("lyxarrow");
629 if (kind_ == NOBREAKDASH)
630 features.require("amsmath");
631 if (kind_ == PHRASE_LYX)
632 features.require("LyX");
636 bool InsetSpecialChar::isChar() const
638 return kind_ != HYPHENATION && kind_ != LIGATURE_BREAK;
642 bool InsetSpecialChar::isLetter() const
644 return kind_ == HYPHENATION || kind_ == LIGATURE_BREAK
645 || kind_ == NOBREAKDASH;
649 bool InsetSpecialChar::isLineSeparator() const
652 // this would be nice, but it does not work, since
653 // Paragraph::stripLeadingSpaces nukes the characters which
654 // have this property. I leave the code here, since it should
655 // eventually be made to work. (JMarc 20020327)
656 return kind_ == HYPHENATION || kind_ == ALLOWBREAK
657 || kind_ == MENU_SEPARATOR || kind_ == SLASH;