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"
21 #include "LaTeXFeatures.h"
23 #include "MetricsInfo.h"
24 #include "output_xhtml.h"
25 #include "texstream.h"
27 #include "frontends/FontMetrics.h"
28 #include "frontends/NullPainter.h"
29 #include "frontends/Painter.h"
31 #include "support/debug.h"
32 #include "support/docstream.h"
39 InsetSpecialChar::InsetSpecialChar(Kind k)
44 InsetSpecialChar::Kind InsetSpecialChar::kind() const
50 docstring InsetSpecialChar::toolTip(BufferView const &, int, int) const
55 message = from_ascii("Optional Line Break (ZWSP)");
58 message = from_ascii("Ligature Break (ZWNJ)");
61 message = from_ascii("End of Sentence");
64 message = from_ascii("Hyphenation Point");
67 message = from_ascii("Breakable Slash");
70 message = from_ascii("Protected Hyphen (SHY)");
78 // no tooltip for these ones.
85 Inset::RowFlags InsetSpecialChar::rowFlags() const
91 // these are the elements that allow line breaking
110 // helper function: draw text and update x.
111 void drawChar(PainterInfo & pi, int & x, int const y, char_type ch)
113 FontInfo font = pi.base.font;
114 font.setPaintColor(pi.textColor(font.realColor()));
115 pi.pain.text(x, y, ch, font);
116 x += theFontMetrics(font).width(ch);
120 void drawLogo(PainterInfo & pi, int & x, int const y, InsetSpecialChar::Kind kind)
122 FontInfo const & font = pi.base.font;
123 int const em = theFontMetrics(font).em();
125 case InsetSpecialChar::PHRASE_LYX:
127 * \providecommand{\LyX}{L\kern-.1667em\lower.25em\hbox{Y}\kern-.125emX\\@};
129 drawChar(pi, x, y, 'L');
131 drawChar(pi, x, y + em / 4, 'Y');
133 drawChar(pi, x, y, 'X');
136 case InsetSpecialChar::PHRASE_TEX: {
138 * \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@}
140 int const ex = theFontMetrics(font).xHeight();
141 drawChar(pi, x, y, 'T');
143 drawChar(pi, x, y + ex / 2, 'E');
145 drawChar(pi, x, y, 'X');
148 case InsetSpecialChar::PHRASE_LATEX2E:
150 * \DeclareRobustCommand{\LaTeXe}{\mbox{\m@th
151 * \if b\expandafter\@car\f@series\@nil\boldmath\fi
152 * \LaTeX\kern.15em2$_{\textstyle\varepsilon}$}}
154 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_LATEX);
156 drawChar(pi, x, y, '2');
157 // ε U+03B5 GREEK SMALL LETTER EPSILON
158 drawChar(pi, x, y + em / 4, char_type(0x03b5));
161 case InsetSpecialChar::PHRASE_LATEX: {
163 * \DeclareRobustCommand{\LaTeX}{L\kern-.36em%
165 * \vbox to\ht\z@{\hbox{\check@mathfonts
166 * \fontsize\sf@size\z@
167 * \math@fontsfalse\selectfont
174 drawChar(pi, x, y, 'L');
176 PainterInfo pi2 = pi;
177 pi2.base.font.decSize().decSize();
178 drawChar(pi2, x, y - em / 5, 'A');
180 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_TEX);
184 LYXERR0("No information for drawing logo " << kind);
191 void InsetSpecialChar::metrics(MetricsInfo & mi, Dimension & dim) const
193 frontend::FontMetrics const & fm =
194 theFontMetrics(mi.base.font);
195 dim.asc = fm.maxAscent();
202 dim.asc = fm.xHeight();
203 dim.des = fm.descent('g');
204 dim.wid = fm.em() / 8;
209 case END_OF_SENTENCE:
213 s = from_ascii(". . .");
216 // ▹ U+25B9 WHITE RIGHT-POINTING SMALL TRIANGLE
217 // There is a \thinspace on each side of the triangle
218 dim.wid = 2 * fm.em() / 6 + fm.width(char_type(0x25B9));
221 dim.wid = fm.width(from_ascii("-"));
223 dim.wid -= 2; // to make it look shorter
227 dim.des = fm.descent(s[0]);
236 dim.asc = fm.maxAscent();
237 dim.des = fm.maxDescent();
238 frontend::NullPainter np;
239 PainterInfo pi(mi.base.bv, np);
240 pi.base.font = mi.base.font;
241 drawLogo(pi, dim.wid, 0, kind_);
245 dim.wid = fm.width(s);
249 void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const
251 FontInfo font = pi.base.font;
256 font.setColor(Color_special);
257 pi.pain.text(x, y, char_type('-'), font);
262 // A small vertical line
263 int const asc = theFontMetrics(pi.base.font).xHeight();
264 int const desc = theFontMetrics(pi.base.font).descent('g');
265 int const x0 = x; // x + 1; // FIXME: incline,
266 int const x1 = x; // x - 1; // similar to LibreOffice?
267 int const y0 = y + desc;
268 int const y1 = y - asc / 3;
269 pi.pain.line(x0, y1, x1, y0, Color_special);
274 font.setColor(Color_special);
275 pi.pain.text(x, y, char_type('|'), font);
278 case END_OF_SENTENCE:
280 font.setColor(Color_special);
281 pi.pain.text(x, y, char_type('.'), font);
286 font.setColor(Color_special);
287 string ell = ". . . ";
288 docstring dell(ell.begin(), ell.end());
289 pi.pain.text(x, y, dell, font);
294 frontend::FontMetrics const & fm =
295 theFontMetrics(font);
297 // There is a \thinspace on each side of the triangle
299 // ▹ U+25B9 WHITE RIGHT-POINTING SMALL TRIANGLE
300 // ◃ U+25C3 WHITE LEFT-POINTING SMALL TRIANGLE
301 char_type const c = pi.ltr_pos ? 0x25B9 : 0x25C3;
302 font.setColor(Color_special);
303 pi.pain.text(x, y, c, font);
308 font.setColor(Color_special);
309 pi.pain.text(x, y, char_type('/'), font);
314 font.setColor(Color_latex);
315 pi.pain.text(x, y, char_type('-'), font);
322 drawLogo(pi, x, y, kind_);
328 void InsetSpecialChar::write(ostream & os) const
333 command = "softhyphen";
336 command = "allowbreak";
339 command = "ligaturebreak";
341 case END_OF_SENTENCE:
342 command = "endofsentence";
348 command = "menuseparator";
351 command = "breakableslash";
354 command = "nobreakdash";
369 os << "\\SpecialChar " << command << "\n";
373 void InsetSpecialChar::read(Lexer & lex)
376 string const command = lex.getString();
378 if (command == "softhyphen")
380 else if (command == "allowbreak")
382 else if (command == "ligaturebreak")
383 kind_ = LIGATURE_BREAK;
384 else if (command == "endofsentence")
385 kind_ = END_OF_SENTENCE;
386 else if (command == "ldots")
388 else if (command == "menuseparator")
389 kind_ = MENU_SEPARATOR;
390 else if (command == "breakableslash")
392 else if (command == "nobreakdash")
394 else if (command == "LyX")
396 else if (command == "TeX")
398 else if (command == "LaTeX2e")
399 kind_ = PHRASE_LATEX2E;
400 else if (command == "LaTeX")
401 kind_ = PHRASE_LATEX;
403 lex.printError("InsetSpecialChar: Unknown kind: `$$Token'");
407 void InsetSpecialChar::latex(otexstream & os,
408 OutputParams const & rp) const
410 bool const rtl = rp.local_font->isRightToLeft();
411 bool const utf8 = rp.encoding->iconvName() == "UTF-8";
413 string lswitche = "";
414 if (rtl && !rp.use_polyglossia) {
417 if (rp.local_font->language()->lang() == "arabic_arabi"
418 || rp.local_font->language()->lang() == "farsi")
419 lswitch = "\\textLR{";
427 // U+200B not yet supported by utf8 inputenc
428 os << "\\LyXZeroWidthSpace" << termcmd;
432 // U+200C ZERO WIDTH NON-JOINER
435 os << "\\textcompwordmark" << termcmd;
437 case END_OF_SENTENCE:
441 os << "\\ldots" << termcmd;
451 os << "\\slash" << termcmd;
456 os << "\\nobreakdash-";
461 os << lswitch << "\\LyX" << termcmd << lswitche;
466 os << lswitch << "\\TeX" << termcmd << lswitche;
471 os << lswitch << "\\LaTeXe" << termcmd << lswitche;
476 os << lswitch << "\\LaTeX" << termcmd << lswitche;
482 int InsetSpecialChar::plaintext(odocstringstream & os,
483 OutputParams const &, size_t) const
489 // U+200B ZERO WIDTH SPACE (ZWSP)
493 // U+200C ZERO WIDTH NON-JOINER
496 case END_OF_SENTENCE:
500 // … U+2026 HORIZONTAL ELLIPSIS
510 // ‑ U+2011 NON-BREAKING HYPHEN
521 // ε U+03B5 GREEK SMALL LETTER EPSILON
532 int InsetSpecialChar::docbook(odocstream & os, OutputParams const &) const
538 // U+200B ZERO WIDTH SPACE (ZWSP)
543 case END_OF_SENTENCE:
566 // ε U+03B5 GREEK SMALL LETTER EPSILON
577 docstring InsetSpecialChar::xhtml(XMLStream & xs, OutputParams const &) const
583 xs << XMLStream::ESCAPE_NONE << "​";
586 xs << XMLStream::ESCAPE_NONE << "‌";
588 case END_OF_SENTENCE:
592 xs << XMLStream::ESCAPE_NONE << "…";
595 xs << XMLStream::ESCAPE_NONE << "⇒";
598 xs << XMLStream::ESCAPE_NONE << "⁄";
601 xs << XMLStream::ESCAPE_NONE << "‑";
610 xs << "LaTeX2" << XMLStream::ESCAPE_NONE << "ε";
620 void InsetSpecialChar::toString(odocstream & os) const
625 // Do not output ZERO WIDTH SPACE and ZERO WIDTH NON JOINER here
626 // Spell checker would choke on it.
631 odocstringstream ods;
632 plaintext(ods, OutputParams(0));
637 void InsetSpecialChar::forOutliner(docstring & os, size_t const,
640 odocstringstream ods;
641 plaintext(ods, OutputParams(0));
646 void InsetSpecialChar::validate(LaTeXFeatures & features) const
648 if (kind_ == ALLOWBREAK)
649 features.require("lyxzerowidthspace");
650 if (kind_ == MENU_SEPARATOR)
651 features.require("lyxarrow");
652 if (kind_ == NOBREAKDASH)
653 features.require("amsmath");
654 if (kind_ == PHRASE_LYX)
655 features.require("LyX");
659 bool InsetSpecialChar::isChar() const
661 return kind_ != HYPHENATION && kind_ != LIGATURE_BREAK;
665 bool InsetSpecialChar::isLetter() const
667 return kind_ == HYPHENATION || kind_ == LIGATURE_BREAK
668 || kind_ == NOBREAKDASH;
672 bool InsetSpecialChar::isLineSeparator() const
675 // this would be nice, but it does not work, since
676 // Paragraph::stripLeadingSpaces nukes the characters which
677 // have this property. I leave the code here, since it should
678 // eventually be made to work. (JMarc 20020327)
679 return kind_ == HYPHENATION || kind_ == ALLOWBREAK
680 || kind_ == MENU_SEPARATOR || kind_ == SLASH;