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"
26 #include "texstream.h"
28 #include "frontends/FontMetrics.h"
29 #include "frontends/NullPainter.h"
30 #include "frontends/Painter.h"
32 #include "support/debug.h"
33 #include "support/docstream.h"
40 InsetSpecialChar::InsetSpecialChar(Kind k)
41 : Inset(nullptr), kind_(k)
45 InsetSpecialChar::Kind InsetSpecialChar::kind() const
51 docstring InsetSpecialChar::toolTip(BufferView const &, int, int) const
56 message = from_ascii("Optional Line Break (ZWSP)");
59 message = from_ascii("Ligature Break (ZWNJ)");
62 message = from_ascii("End of Sentence");
65 message = from_ascii("Hyphenation Point");
68 message = from_ascii("Breakable Slash");
71 message = from_ascii("Protected Hyphen (SHY)");
79 // no tooltip for these ones.
86 int InsetSpecialChar::rowFlags() const
92 // these are the elements that allow line breaking
111 // helper function: draw text and update x.
112 void drawChar(PainterInfo & pi, int & x, int const y, char_type ch)
114 FontInfo font = pi.base.font;
115 font.setPaintColor(pi.textColor(font.realColor()));
116 pi.pain.text(x, y, ch, font);
117 x += theFontMetrics(font).width(ch);
121 void drawLogo(PainterInfo & pi, int & x, int const y, InsetSpecialChar::Kind kind)
123 FontInfo const & font = pi.base.font;
124 int const em = theFontMetrics(font).em();
126 case InsetSpecialChar::PHRASE_LYX:
128 * \providecommand{\LyX}{L\kern-.1667em\lower.25em\hbox{Y}\kern-.125emX\\@};
130 drawChar(pi, x, y, 'L');
132 drawChar(pi, x, y + em / 4, 'Y');
134 drawChar(pi, x, y, 'X');
137 case InsetSpecialChar::PHRASE_TEX: {
139 * \def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX\@}
141 int const ex = theFontMetrics(font).xHeight();
142 drawChar(pi, x, y, 'T');
144 drawChar(pi, x, y + ex / 2, 'E');
146 drawChar(pi, x, y, 'X');
149 case InsetSpecialChar::PHRASE_LATEX2E:
151 * \DeclareRobustCommand{\LaTeXe}{\mbox{\m@th
152 * \if b\expandafter\@car\f@series\@nil\boldmath\fi
153 * \LaTeX\kern.15em2$_{\textstyle\varepsilon}$}}
155 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_LATEX);
157 drawChar(pi, x, y, '2');
158 // ε U+03B5 GREEK SMALL LETTER EPSILON
159 drawChar(pi, x, y + em / 4, char_type(0x03b5));
162 case InsetSpecialChar::PHRASE_LATEX: {
164 * \DeclareRobustCommand{\LaTeX}{L\kern-.36em%
166 * \vbox to\ht\z@{\hbox{\check@mathfonts
167 * \fontsize\sf@size\z@
168 * \math@fontsfalse\selectfont
175 drawChar(pi, x, y, 'L');
177 PainterInfo pi2 = pi;
178 pi2.base.font.decSize().decSize();
179 drawChar(pi2, x, y - em / 5, 'A');
181 drawLogo(pi, x, y, InsetSpecialChar::PHRASE_TEX);
185 LYXERR0("No information for drawing logo " << kind);
192 void InsetSpecialChar::metrics(MetricsInfo & mi, Dimension & dim) const
194 frontend::FontMetrics const & fm =
195 theFontMetrics(mi.base.font);
196 dim.asc = fm.maxAscent();
203 dim.asc = fm.xHeight();
204 dim.des = fm.descent('g');
205 dim.wid = fm.em() / 8;
210 case END_OF_SENTENCE:
214 s = from_ascii(". . .");
217 // ▹ U+25B9 WHITE RIGHT-POINTING SMALL TRIANGLE
218 // There is a \thinspace on each side of the triangle
219 dim.wid = 2 * fm.em() / 6 + fm.width(char_type(0x25B9));
222 dim.wid = fm.width(from_ascii("-"));
224 dim.wid -= 2; // to make it look shorter
228 dim.des = fm.descent(s[0]);
237 dim.asc = fm.maxAscent();
238 dim.des = fm.maxDescent();
239 frontend::NullPainter np;
240 PainterInfo pi(mi.base.bv, np);
241 pi.base.font = mi.base.font;
242 drawLogo(pi, dim.wid, 0, kind_);
246 dim.wid = fm.width(s);
250 void InsetSpecialChar::draw(PainterInfo & pi, int x, int y) const
252 FontInfo font = pi.base.font;
257 font.setColor(Color_special);
258 pi.pain.text(x, y, char_type('-'), font);
263 // A small vertical line
264 int const asc = theFontMetrics(pi.base.font).xHeight();
265 int const desc = theFontMetrics(pi.base.font).descent('g');
266 int const x0 = x; // x + 1; // FIXME: incline,
267 int const x1 = x; // x - 1; // similar to LibreOffice?
268 int const y0 = y + desc;
269 int const y1 = y - asc / 3;
270 pi.pain.line(x0, y1, x1, y0, Color_special);
275 font.setColor(Color_special);
276 pi.pain.text(x, y, char_type('|'), font);
279 case END_OF_SENTENCE:
281 font.setColor(Color_special);
282 pi.pain.text(x, y, char_type('.'), font);
287 font.setColor(Color_special);
288 string ell = ". . . ";
289 docstring dell(ell.begin(), ell.end());
290 pi.pain.text(x, y, dell, font);
295 frontend::FontMetrics const & fm =
296 theFontMetrics(font);
298 // There is a \thinspace on each side of the triangle
300 // ▹ U+25B9 WHITE RIGHT-POINTING SMALL TRIANGLE
301 // ◃ U+25C3 WHITE LEFT-POINTING SMALL TRIANGLE
302 char_type const c = pi.ltr_pos ? 0x25B9 : 0x25C3;
303 font.setColor(Color_special);
304 pi.pain.text(x, y, c, font);
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
411 bool const rtl = rp.local_font->isRightToLeft();
412 bool const utf8 = rp.encoding->iconvName() == "UTF-8";
414 string lswitche = "";
415 if (rtl && !rp.use_polyglossia) {
418 if (rp.local_font->language()->lang() == "arabic_arabi"
419 || rp.local_font->language()->lang() == "farsi")
420 lswitch = "\\textLR{";
428 // U+200B not yet supported by utf8 inputenc
429 os << "\\LyXZeroWidthSpace" << termcmd;
433 // U+200C ZERO WIDTH NON-JOINER
436 os << "\\textcompwordmark" << termcmd;
438 case END_OF_SENTENCE:
442 os << "\\ldots" << termcmd;
452 os << "\\slash" << termcmd;
457 os << "\\nobreakdash-";
462 os << lswitch << "\\LyX" << termcmd << lswitche;
467 os << lswitch << "\\TeX" << termcmd << lswitche;
472 os << lswitch << "\\LaTeXe" << termcmd << lswitche;
477 os << lswitch << "\\LaTeX" << termcmd << lswitche;
483 int InsetSpecialChar::plaintext(odocstringstream & os,
484 OutputParams const &, size_t) const
490 // U+200B ZERO WIDTH SPACE (ZWSP)
494 // U+200C ZERO WIDTH NON-JOINER
497 case END_OF_SENTENCE:
501 // … U+2026 HORIZONTAL ELLIPSIS
511 // ‑ U+2011 NON-BREAKING HYPHEN
522 // ε U+03B5 GREEK SMALL LETTER EPSILON
533 void InsetSpecialChar::docbook(XMLStream & xs, OutputParams const &) const
538 xs << XMLStream::ESCAPE_NONE << "­";
542 xs << XMLStream::ESCAPE_NONE << "​";
545 // Zero width non-joiner
546 xs << XMLStream::ESCAPE_NONE << "‌";
548 case END_OF_SENTENCE:
553 xs << XMLStream::ESCAPE_NONE << "…";
556 // ⇒, right arrow.
557 xs << XMLStream::ESCAPE_NONE << "⇒";
560 // ⁄, fractional slash.
561 xs << XMLStream::ESCAPE_NONE << "⁄";
563 // Non-breaking hyphen.
565 xs << XMLStream::ESCAPE_NONE << "‑";
574 // Lower-case epsilon.
575 xs << "LaTeX2" << XMLStream::ESCAPE_NONE << "ε";
584 docstring InsetSpecialChar::xhtml(XMLStream & xs, OutputParams const &) const
590 xs << XMLStream::ESCAPE_NONE << "​";
593 xs << XMLStream::ESCAPE_NONE << "‌";
595 case END_OF_SENTENCE:
599 xs << XMLStream::ESCAPE_NONE << "…";
602 xs << XMLStream::ESCAPE_NONE << "⇒";
605 xs << XMLStream::ESCAPE_NONE << "⁄";
608 xs << XMLStream::ESCAPE_NONE << "‑";
617 xs << "LaTeX2" << XMLStream::ESCAPE_NONE << "ε";
627 void InsetSpecialChar::toString(odocstream & os) const
632 // Do not output ZERO WIDTH SPACE and ZERO WIDTH NON JOINER here
633 // Spell checker would choke on it.
638 odocstringstream ods;
639 plaintext(ods, OutputParams(0));
644 void InsetSpecialChar::forOutliner(docstring & os, size_t const,
647 odocstringstream ods;
648 plaintext(ods, OutputParams(0));
653 void InsetSpecialChar::validate(LaTeXFeatures & features) const
655 if (kind_ == ALLOWBREAK)
656 features.require("lyxzerowidthspace");
657 if (kind_ == MENU_SEPARATOR)
658 features.require("lyxarrow");
659 if (kind_ == NOBREAKDASH)
660 features.require("amsmath");
661 if (kind_ == PHRASE_LYX)
662 features.require("LyX");
666 bool InsetSpecialChar::isChar() const
668 return kind_ != HYPHENATION && kind_ != LIGATURE_BREAK;
672 bool InsetSpecialChar::isLetter() const
674 return kind_ == HYPHENATION || kind_ == LIGATURE_BREAK
675 || kind_ == NOBREAKDASH
676 || kind_ == PHRASE_LYX || kind_ == PHRASE_LATEX
677 || kind_ == PHRASE_TEX || kind_ == PHRASE_LATEX2E;
681 bool InsetSpecialChar::isLineSeparator() const
684 // this would be nice, but it does not work, since
685 // Paragraph::stripLeadingSpaces nukes the characters which
686 // have this property. I leave the code here, since it should
687 // eventually be made to work. (JMarc 20020327)
688 return kind_ == HYPHENATION || kind_ == ALLOWBREAK
689 || kind_ == MENU_SEPARATOR || kind_ == SLASH;