File Improve-built-in-line_block-characters-drawing.patch of Package konsole

From 13132fc77bf90d0a39c62676f3ac75caa581962f Mon Sep 17 00:00:00 2001
From: Mariusz Glebocki <mglb@arccos-1.net>
Date: Thu, 7 Feb 2019 01:36:52 +0100
Subject: Improve built-in line/block characters drawing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Summary:
* Fix bold lines (BUG 402415).
* Make drawing pixel-perfect.
* Make line width proportional to font size.
* Move relevant code to separate file and namespace.
* Remove code for checking supported line characters from Character
  class. Information about what is supported is now in one place
  together width drawing code.
* Remove fontembedder/LineFont files (no longer used).
* Add test script for displaying supported characters table.
* Add triple and quadruple dashes (U+2504...U+250B).
* Change shade block characters (U+2591...U+2593) look. When
  antialiasing is turned on, shades are drawn as transculent solid
  rectangles with 25%, 50% and 75% alpha. This matches the characters
  name/description and their usage. Without antialiasing, previous
  method with patterns is used.

**Screenshots**

Font size: 10pt; character width: 8px
{F6602823}

Font size: 11pt; character width: 9px
{F6602824}

Font size: 12pt; character width: 10px
{F6602825}

Font size: 13-14pt; character width: 11px; w/o antialiasing
{F6602826}

Font size: 13-14pt; character width: 11px
{F6602827}

Font size: 15pt; character width: 12px
{F6602828}

Font size: 6-7pt; character width: 5px
{F6602829}

Font size: 8-9pt; character width: 7px; w/o antialiasing
{F6602830}

Font size: 8-9pt; character width: 7px
{F6602831}

Alignment test (8pt)
{F6602832}

Note: Copyrights in LineBlockCharactersDrawer.cpp are based on
`git blame -w src/TerminalDisplay.cpp` executed before moving the code
to a separate file. Years from first/last commit. Authors sorted by
year. Whitespace-only changes were ignored. Maksim's code was commited
by Waldo Bastian who mentioned him as the author in commit message
(see 5062b40dd).

BUG: 402415

Test Plan:
== Common steps for all tests ==

* Open //Edit Current Profile → Appearance//.
* Turn on //Draw intense colors in bold font//.
* Turn off //Use line characters contained in font//.
* (Optional) select a font which is able to display bold characters in
  Konsole (e.g. DejaVu Sans Mono).

== Check characters validity ==

* Run `./tests/line_block_characters_table.py`.
* Open //Edit Current Profile → Appearance//.
* By switching //Use line characters contained in font// on and off,
  compare built-in characters drawing with characters from a font.
  General shape and line directions must be the same. Small offsets,
  line width differences (as long as proportions between lines in
  a character are kept), and quality differences are allowed.

== Review overall quality ==

* Run `./tests/line_block_characters_table.py`.
* Review glyphs quality in different font sizes.
* Open //Edit Current Profile → Appearance//.
* Toggle //Smooth fonts//, review quality again.

== Check alignment ==

* Display `tests/UTF-8-demo.txt`
* At the bottom of the file you can find a few alignment images. Check
  if all lines align properly. If you're unsure how it should look,
  compare it with font characters by turning on //Use line characters
  contained in font// option.

Reviewers: #konsole, #vdg, fvogt, hindenburg

Reviewed By: #konsole, hindenburg

Subscribers: hindenburg, sandsmark, fvogt, konsole-devel

Tags: #konsole

Differential Revision: https://phabricator.kde.org/D18735
---
 src/CMakeLists.txt                   |   3 +-
 src/Character.h                      |  24 --
 src/LineBlockCharacters.cpp          | 722 ++++++++++++++++++++++++++++++++
 src/LineBlockCharacters.h            |  57 +++
 src/LineFont.h                       |  21 -
 src/LineFont.src                     | 786 -----------------------------------
 src/TerminalDisplay.cpp              | 392 +----------------
 tests/line_block_characters_table.py |  67 +++
 tools/CMakeLists.txt                 |  15 -
 tools/fontembedder.cpp               | 138 ------
 10 files changed, 862 insertions(+), 1363 deletions(-)
 create mode 100644 src/LineBlockCharacters.cpp
 create mode 100644 src/LineBlockCharacters.h
 delete mode 100644 src/LineFont.h
 delete mode 100644 src/LineFont.src
 create mode 100755 tests/line_block_characters_table.py
 delete mode 100644 tools/fontembedder.cpp

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5b366c6..eae3344 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -34,8 +34,6 @@ endif()
 option(REMOVE_SENDTEXT_RUNCOMMAND_DBUS_METHODS "Konsole: remove sendText and runCommand dbus methods" OFF)
 
 ### Development tools
-option(KONSOLE_BUILD_FONTEMBEDDER "Konsole: build fontembedder executable" OFF)
-option(KONSOLE_GENERATE_LINEFONT "Konsole: regenerate LineFont file" OFF)
 option(KONSOLE_BUILD_UNI2CHARACTERWIDTH "Konsole: build uni2characterwidth executable" OFF)
 
 ### Konsole source files shared between embedded terminal and main application
@@ -99,6 +97,7 @@ set(konsoleprivate_SRCS ${sessionadaptors_SRCS}
                         ExtendedCharTable.cpp
                         TerminalDisplay.cpp
                         TerminalDisplayAccessible.cpp
+                        LineBlockCharacters.cpp
                         ViewContainer.cpp
                         ViewManager.cpp
                         ViewProperties.cpp
diff --git a/src/Character.h b/src/Character.h
index ed54715..3e94c8f 100644
--- a/src/Character.h
+++ b/src/Character.h
@@ -54,21 +54,6 @@ const RenditionFlags RE_CONCEAL        = (1 << 9);
 const RenditionFlags RE_OVERLINE       = (1 << 10);
 
 /**
- * Unicode character in the range of U+2500 ~ U+257F are known as line
- * characters, or box-drawing characters. Currently, konsole draws those
- * characters itself, instead of using the glyph provided by the font.
- * Unfortunately, the triple and quadruple dash lines (┄┅┆┇┈┉┊┋) are too
- * detailed too be drawn cleanly at normal font scales without anti
- * -aliasing, so those are drawn as regular characters.
- */
-inline bool isSupportedLineChar(uint codePoint)
-{
-    return ((codePoint & 0xFF80) == 0x2500 // Unicode block: Mathematical Symbols - Box Drawing
-           && !(0x2504 <= codePoint && codePoint <= 0x250B)) || // Triple and quadruple dash range
-        (codePoint >= 0x2580 && codePoint <= 0x259F); // Block characters
-}
-
-/**
  * A single character in the terminal which consists of a unicode character
  * value, foreground and background colors and a set of rendition attributes
  * which specify how it should be drawn.
@@ -143,15 +128,6 @@ public:
      */
     friend bool operator !=(const Character &a, const Character &b);
 
-    inline bool isLineChar() const
-    {
-        if (rendition & RE_EXTENDED_CHAR) {
-            return false;
-        } else {
-            return isSupportedLineChar(character);
-        }
-    }
-
     inline bool isSpace() const
     {
         if (rendition & RE_EXTENDED_CHAR) {
diff --git a/src/LineBlockCharacters.cpp b/src/LineBlockCharacters.cpp
new file mode 100644
index 0000000..a562f45
--- /dev/null
+++ b/src/LineBlockCharacters.cpp
@@ -0,0 +1,736 @@
+/*
+    This file is part of Konsole, a terminal emulator for KDE.
+
+    Copyright 2018-2019 by Mariusz Glebocki <mglb@arccos-1.net>
+    Copyright 2018 by Martin T. H. Sandsmark <martin.sandsmark@kde.org>
+    Copyright 2015-2018 by Kurt Hindenburg <kurt.hindenburg@gmail.com>
+    Copyright 2005 by Maksim Orlovich
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+    02110-1301  USA.
+*/
+
+// Own
+#include "LineBlockCharacters.h"
+
+// Qt
+#include <QPainter>
+
+namespace Konsole {
+namespace LineBlockCharacters {
+
+enum LineType {
+    LtNone   = 0,
+    LtDouble = 1,
+    LtLight  = 2,
+    LtHeavy  = 3,
+};
+
+// PackedLineTypes is an 8-bit number representing types of 4 line character's lines. Each line is
+// represented by 2 bits. Lines order, starting from MSB: top, right, bottom, left.
+static inline constexpr quint8 makePackedLineTypes(LineType top, LineType right, LineType bottom, LineType left)
+{
+    return (int(top) & 3) << 6 | (int(right) & 3) << 4 | (int(bottom) & 3) << 2 | (int(left) & 3);
+}
+
+static constexpr const quint8 PackedLineTypesLut[] = {
+    //                  top       right     bottom    left
+    makePackedLineTypes(LtNone  , LtLight , LtNone  , LtLight ), /* U+2500 ─ */
+    makePackedLineTypes(LtNone  , LtHeavy , LtNone  , LtHeavy ), /* U+2501 ━ */
+    makePackedLineTypes(LtLight , LtNone  , LtLight , LtNone  ), /* U+2502 │ */
+    makePackedLineTypes(LtHeavy , LtNone  , LtHeavy , LtNone  ), /* U+2503 ┃ */
+    0, 0, 0, 0, 0, 0, 0, 0, /* U+2504-0x250b */
+    makePackedLineTypes(LtNone  , LtLight , LtLight , LtNone  ), /* U+250C ┌ */
+    makePackedLineTypes(LtNone  , LtHeavy , LtLight , LtNone  ), /* U+250D ┍ */
+    makePackedLineTypes(LtNone  , LtLight , LtHeavy , LtNone  ), /* U+250E ┎ */
+    makePackedLineTypes(LtNone  , LtHeavy , LtHeavy , LtNone  ), /* U+250F ┏ */
+    makePackedLineTypes(LtNone  , LtNone  , LtLight , LtLight ), /* U+2510 ┐ */
+    makePackedLineTypes(LtNone  , LtNone  , LtLight , LtHeavy ), /* U+2511 ┑ */
+    makePackedLineTypes(LtNone  , LtNone  , LtHeavy , LtLight ), /* U+2512 ┒ */
+    makePackedLineTypes(LtNone  , LtNone  , LtHeavy , LtHeavy ), /* U+2513 ┓ */
+    makePackedLineTypes(LtLight , LtLight , LtNone  , LtNone  ), /* U+2514 └ */
+    makePackedLineTypes(LtLight , LtHeavy , LtNone  , LtNone  ), /* U+2515 ┕ */
+    makePackedLineTypes(LtHeavy , LtLight , LtNone  , LtNone  ), /* U+2516 ┖ */
+    makePackedLineTypes(LtHeavy , LtHeavy , LtNone  , LtNone  ), /* U+2517 ┗ */
+    makePackedLineTypes(LtLight , LtNone  , LtNone  , LtLight ), /* U+2518 ┘ */
+    makePackedLineTypes(LtLight , LtNone  , LtNone  , LtHeavy ), /* U+2519 ┙ */
+    makePackedLineTypes(LtHeavy , LtNone  , LtNone  , LtLight ), /* U+251A ┚ */
+    makePackedLineTypes(LtHeavy , LtNone  , LtNone  , LtHeavy ), /* U+251B ┛ */
+    makePackedLineTypes(LtLight , LtLight , LtLight , LtNone  ), /* U+251C ├ */
+    makePackedLineTypes(LtLight , LtHeavy , LtLight , LtNone  ), /* U+251D ┝ */
+    makePackedLineTypes(LtHeavy , LtLight , LtLight , LtNone  ), /* U+251E ┞ */
+    makePackedLineTypes(LtLight , LtLight , LtHeavy , LtNone  ), /* U+251F ┟ */
+    makePackedLineTypes(LtHeavy , LtLight , LtHeavy , LtNone  ), /* U+2520 ┠ */
+    makePackedLineTypes(LtHeavy , LtHeavy , LtLight , LtNone  ), /* U+2521 ┡ */
+    makePackedLineTypes(LtLight , LtHeavy , LtHeavy , LtNone  ), /* U+2522 ┢ */
+    makePackedLineTypes(LtHeavy , LtHeavy , LtHeavy , LtNone  ), /* U+2523 ┣ */
+    makePackedLineTypes(LtLight , LtNone  , LtLight , LtLight ), /* U+2524 ┤ */
+    makePackedLineTypes(LtLight , LtNone  , LtLight , LtHeavy ), /* U+2525 ┥ */
+    makePackedLineTypes(LtHeavy , LtNone  , LtLight , LtLight ), /* U+2526 ┦ */
+    makePackedLineTypes(LtLight , LtNone  , LtHeavy , LtLight ), /* U+2527 ┧ */
+    makePackedLineTypes(LtHeavy , LtNone  , LtHeavy , LtLight ), /* U+2528 ┨ */
+    makePackedLineTypes(LtHeavy , LtNone  , LtLight , LtHeavy ), /* U+2529 ┩ */
+    makePackedLineTypes(LtLight , LtNone  , LtHeavy , LtHeavy ), /* U+252A ┪ */
+    makePackedLineTypes(LtHeavy , LtNone  , LtHeavy , LtHeavy ), /* U+252B ┫ */
+    makePackedLineTypes(LtNone  , LtLight , LtLight , LtLight ), /* U+252C ┬ */
+    makePackedLineTypes(LtNone  , LtLight , LtLight , LtHeavy ), /* U+252D ┭ */
+    makePackedLineTypes(LtNone  , LtHeavy , LtLight , LtLight ), /* U+252E ┮ */
+    makePackedLineTypes(LtNone  , LtHeavy , LtLight , LtHeavy ), /* U+252F ┯ */
+    makePackedLineTypes(LtNone  , LtLight , LtHeavy , LtLight ), /* U+2530 ┰ */
+    makePackedLineTypes(LtNone  , LtLight , LtHeavy , LtHeavy ), /* U+2531 ┱ */
+    makePackedLineTypes(LtNone  , LtHeavy , LtHeavy , LtLight ), /* U+2532 ┲ */
+    makePackedLineTypes(LtNone  , LtHeavy , LtHeavy , LtHeavy ), /* U+2533 ┳ */
+    makePackedLineTypes(LtLight , LtLight , LtNone  , LtLight ), /* U+2534 ┴ */
+    makePackedLineTypes(LtLight , LtLight , LtNone  , LtHeavy ), /* U+2535 ┵ */
+    makePackedLineTypes(LtLight , LtHeavy , LtNone  , LtLight ), /* U+2536 ┶ */
+    makePackedLineTypes(LtLight , LtHeavy , LtNone  , LtHeavy ), /* U+2537 ┷ */
+    makePackedLineTypes(LtHeavy , LtLight , LtNone  , LtLight ), /* U+2538 ┸ */
+    makePackedLineTypes(LtHeavy , LtLight , LtNone  , LtHeavy ), /* U+2539 ┹ */
+    makePackedLineTypes(LtHeavy , LtHeavy , LtNone  , LtLight ), /* U+253A ┺ */
+    makePackedLineTypes(LtHeavy , LtHeavy , LtNone  , LtHeavy ), /* U+253B ┻ */
+    makePackedLineTypes(LtLight , LtLight , LtLight , LtLight ), /* U+253C ┼ */
+    makePackedLineTypes(LtLight , LtLight , LtLight , LtHeavy ), /* U+253D ┽ */
+    makePackedLineTypes(LtLight , LtHeavy , LtLight , LtLight ), /* U+253E ┾ */
+    makePackedLineTypes(LtLight , LtHeavy , LtLight , LtHeavy ), /* U+253F ┿ */
+    makePackedLineTypes(LtHeavy , LtLight , LtLight , LtLight ), /* U+2540 ╀ */
+    makePackedLineTypes(LtLight , LtLight , LtHeavy , LtLight ), /* U+2541 ╁ */
+    makePackedLineTypes(LtHeavy , LtLight , LtHeavy , LtLight ), /* U+2542 ╂ */
+    makePackedLineTypes(LtHeavy , LtLight , LtLight , LtHeavy ), /* U+2543 ╃ */
+    makePackedLineTypes(LtHeavy , LtHeavy , LtLight , LtLight ), /* U+2544 ╄ */
+    makePackedLineTypes(LtLight , LtLight , LtHeavy , LtHeavy ), /* U+2545 ╅ */
+    makePackedLineTypes(LtLight , LtHeavy , LtHeavy , LtLight ), /* U+2546 ╆ */
+    makePackedLineTypes(LtHeavy , LtHeavy , LtLight , LtHeavy ), /* U+2547 ╇ */
+    makePackedLineTypes(LtLight , LtHeavy , LtHeavy , LtHeavy ), /* U+2548 ╈ */
+    makePackedLineTypes(LtHeavy , LtLight , LtHeavy , LtHeavy ), /* U+2549 ╉ */
+    makePackedLineTypes(LtHeavy , LtHeavy , LtHeavy , LtLight ), /* U+254A ╊ */
+    makePackedLineTypes(LtHeavy , LtHeavy , LtHeavy , LtHeavy ), /* U+254B ╋ */
+    0, 0, 0, 0, /* U+254C - U+254F */
+    makePackedLineTypes(LtNone  , LtDouble, LtNone  , LtDouble), /* U+2550 ═ */
+    makePackedLineTypes(LtDouble, LtNone  , LtDouble, LtNone  ), /* U+2551 ║ */
+    makePackedLineTypes(LtNone  , LtDouble, LtLight , LtNone  ), /* U+2552 ╒ */
+    makePackedLineTypes(LtNone  , LtLight , LtDouble, LtNone  ), /* U+2553 ╓ */
+    makePackedLineTypes(LtNone  , LtDouble, LtDouble, LtNone  ), /* U+2554 ╔ */
+    makePackedLineTypes(LtNone  , LtNone  , LtLight , LtDouble), /* U+2555 ╕ */
+    makePackedLineTypes(LtNone  , LtNone  , LtDouble, LtLight ), /* U+2556 ╖ */
+    makePackedLineTypes(LtNone  , LtNone  , LtDouble, LtDouble), /* U+2557 ╗ */
+    makePackedLineTypes(LtLight , LtDouble, LtNone  , LtNone  ), /* U+2558 ╘ */
+    makePackedLineTypes(LtDouble, LtLight , LtNone  , LtNone  ), /* U+2559 ╙ */
+    makePackedLineTypes(LtDouble, LtDouble, LtNone  , LtNone  ), /* U+255A ╚ */
+    makePackedLineTypes(LtLight , LtNone  , LtNone  , LtDouble), /* U+255B ╛ */
+    makePackedLineTypes(LtDouble, LtNone  , LtNone  , LtLight ), /* U+255C ╜ */
+    makePackedLineTypes(LtDouble, LtNone  , LtNone  , LtDouble), /* U+255D ╝ */
+    makePackedLineTypes(LtLight , LtDouble, LtLight , LtNone  ), /* U+255E ╞ */
+    makePackedLineTypes(LtDouble, LtLight , LtDouble, LtNone  ), /* U+255F ╟ */
+    makePackedLineTypes(LtDouble, LtDouble, LtDouble, LtNone  ), /* U+2560 ╠ */
+    makePackedLineTypes(LtLight , LtNone  , LtLight , LtDouble), /* U+2561 ╡ */
+    makePackedLineTypes(LtDouble, LtNone  , LtDouble, LtLight ), /* U+2562 ╢ */
+    makePackedLineTypes(LtDouble, LtNone  , LtDouble, LtDouble), /* U+2563 ╣ */
+    makePackedLineTypes(LtNone  , LtDouble, LtLight , LtDouble), /* U+2564 ╤ */
+    makePackedLineTypes(LtNone  , LtLight , LtDouble, LtLight ), /* U+2565 ╥ */
+    makePackedLineTypes(LtNone  , LtDouble, LtDouble, LtDouble), /* U+2566 ╦ */
+    makePackedLineTypes(LtLight , LtDouble, LtNone  , LtDouble), /* U+2567 ╧ */
+    makePackedLineTypes(LtDouble, LtLight , LtNone  , LtLight ), /* U+2568 ╨ */
+    makePackedLineTypes(LtDouble, LtDouble, LtNone  , LtDouble), /* U+2569 ╩ */
+    makePackedLineTypes(LtLight , LtDouble, LtLight , LtDouble), /* U+256A ╪ */
+    makePackedLineTypes(LtDouble, LtLight , LtDouble, LtLight ), /* U+256B ╫ */
+    makePackedLineTypes(LtDouble, LtDouble, LtDouble, LtDouble), /* U+256C ╬ */
+    0, 0, 0, 0, 0, 0, 0, /* U+256D - U+2573 */
+    makePackedLineTypes(LtNone  , LtNone  , LtNone  , LtLight ), /* U+2574 ╴ */
+    makePackedLineTypes(LtLight , LtNone  , LtNone  , LtNone  ), /* U+2575 ╵ */
+    makePackedLineTypes(LtNone  , LtLight , LtNone  , LtNone  ), /* U+2576 ╶ */
+    makePackedLineTypes(LtNone  , LtNone  , LtLight , LtNone  ), /* U+2577 ╷ */
+    makePackedLineTypes(LtNone  , LtNone  , LtNone  , LtHeavy ), /* U+2578 ╸ */
+    makePackedLineTypes(LtHeavy , LtNone  , LtNone  , LtNone  ), /* U+2579 ╹ */
+    makePackedLineTypes(LtNone  , LtHeavy , LtNone  , LtNone  ), /* U+257A ╺ */
+    makePackedLineTypes(LtNone  , LtNone  , LtHeavy , LtNone  ), /* U+257B ╻ */
+    makePackedLineTypes(LtNone  , LtHeavy , LtNone  , LtLight ), /* U+257C ╼ */
+    makePackedLineTypes(LtLight , LtNone  , LtHeavy , LtNone  ), /* U+257D ╽ */
+    makePackedLineTypes(LtNone  , LtLight , LtNone  , LtHeavy ), /* U+257E ╾ */
+    makePackedLineTypes(LtHeavy , LtNone  , LtLight , LtNone  ), /* U+257F ╿ */
+};
+
+
+
+// Bitwise rotate left
+template <typename T>
+inline static T rotateBitsLeft(T value, quint8 amount)
+{
+    static_assert (std::is_unsigned<T>(), "T must be unsigned type");
+    Q_ASSERT(amount < sizeof(value) * 8);
+    return value << amount | value >> (sizeof(value) * 8 - amount);
+}
+
+inline static const QPen pen(const QPainter &paint, uint lineWidth)
+{
+    return QPen(paint.pen().brush(), lineWidth, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
+}
+
+
+
+static inline uint lineWidth(uint fontWidth, bool heavy, bool bold)
+{
+    static const qreal LightWidthToFontWidthRatio = 1.0 / 6.5;
+    static const qreal HeavyHalfExtraToLightRatio = 1.0 / 3.0;
+    static const qreal BoldCoefficient            = 1.5;
+
+    //        ▄▄▄▄▄▄▄ } heavyHalfExtraWidth  ⎫
+    // ██████████████ } lightWidth           ⎬ heavyWidth
+    //        ▀▀▀▀▀▀▀                        ⎭
+    //  light  heavy
+
+    const qreal baseWidth           = fontWidth * LightWidthToFontWidthRatio;
+    const qreal boldCoeff           = bold ? BoldCoefficient : 1.0;
+    // Unless font size is too small, make bold lines at least 1px wider than regular lines
+    const qreal minWidth            = bold && fontWidth >= 7 ? baseWidth + 1.0 : 1.0;
+    const uint  lightWidth          = qRound(qMax(baseWidth * boldCoeff, minWidth));
+    const uint  heavyHalfExtraWidth = qRound(qMax(lightWidth * HeavyHalfExtraToLightRatio, 1.0));
+
+    return heavy ? lightWidth + 2 * heavyHalfExtraWidth : lightWidth;
+}
+
+// Draws characters composed of straight solid lines
+static bool drawBasicLineCharacter(QPainter& paint, int x, int y, int w, int h, uchar code,
+                                   bool bold)
+{
+    quint8 packedLineTypes = code >= sizeof(PackedLineTypesLut) ? 0 : PackedLineTypesLut[code];
+    if (packedLineTypes == 0) {
+        return false;
+    }
+
+    const uint lightLineWidth      = lineWidth(w, false, bold);
+    const uint heavyLineWidth      = lineWidth(w, true, bold);
+    // Distance from double line's parallel axis to each line's parallel axis
+    const uint doubleLinesDistance = lightLineWidth;
+
+    const QPen lightPen = pen(paint, lightLineWidth);
+    const QPen heavyPen = pen(paint, heavyLineWidth);
+
+    static constexpr const unsigned LinesNum = 4;
+
+    // Pixel aligned center point
+    const QPointF center = {
+        x + int(w/2) + 0.5 * (lightLineWidth % 2),
+        y + int(h/2) + 0.5 * (lightLineWidth % 2),
+    };
+
+    // Lines starting points, on the cell edges
+    const QPointF origin[] = {
+        QPointF(center.x(), y             ),
+        QPointF(x+w       , center.y()    ),
+        QPointF(center.x(), y+h           ),
+        QPointF(x         , center.y()    ),
+    };
+    // Unit vectors with directions from center to the line's origin point
+    static const QPointF dir[] = {{0, -1}, {1, 0}, {0, 1}, {-1, 0}};
+
+    const auto removeLineType = [&packedLineTypes](quint8 lineId)->void {
+        lineId = LinesNum - 1 - lineId % LinesNum;
+        packedLineTypes &= ~(3 << (2 * lineId));
+    };
+    const auto getLineType = [&packedLineTypes](quint8 lineId)->LineType {
+        lineId = LinesNum - 1 - lineId % LinesNum;
+        return LineType(packedLineTypes >> 2 * lineId & 3);
+    };
+
+    QPainterPath lightPath; // PainterPath for light lines (Painter Path Light)
+    QPainterPath heavyPath; // PainterPath for heavy lines (Painter Path Heavy)
+    // Returns ppl or pph depending on line type
+    const auto pathForLine = [&](quint8 lineId) -> QPainterPath& {
+        Q_ASSERT(getLineType(lineId) != LtNone);
+        return getLineType(lineId) == LtHeavy ? heavyPath : lightPath;
+    };
+
+    // Process all single up-down/left-right lines for every character that has them. Doing it here
+    // reduces amount of combinations below.
+    // Fully draws: ╋ ╂ ┃ ┿ ┼ │ ━ ─
+    for (unsigned topIndex = 0; topIndex < LinesNum/2; topIndex++) {
+        unsigned iB = (topIndex + 2) % LinesNum;
+        const bool isSingleLine = (getLineType(topIndex) == LtLight
+                                || getLineType(topIndex) == LtHeavy);
+        if (isSingleLine && getLineType(topIndex) == getLineType(iB)) {
+            pathForLine(topIndex).moveTo(origin[topIndex]);
+            pathForLine(topIndex).lineTo(origin[iB]);
+            removeLineType(topIndex);
+            removeLineType(iB);
+        }
+    }
+
+    // Find base rotation of a character and map rotated line indices to the original rotation's
+    // indices. The base rotation is defined as the one with largest packedLineTypes value. This way
+    // we can use the same code for drawing 4 possible character rotations (see switch() below)
+
+    uint topIndex = 0; // index of an original top line in a base rotation
+    quint8 basePackedLineTypes = packedLineTypes;
+    for (uint i = 0; i < LinesNum; i++) {
+        const quint8 rotatedPackedLineTypes = rotateBitsLeft(packedLineTypes, i * 2);
+        if (rotatedPackedLineTypes > basePackedLineTypes) {
+            topIndex = i;
+            basePackedLineTypes = rotatedPackedLineTypes;
+        }
+    }
+    uint rightIndex  = (topIndex + 1) % LinesNum;
+    uint bottomIndex = (topIndex + 2) % LinesNum;
+    uint leftIndex   = (topIndex + 3) % LinesNum;
+
+    // Common paths
+    const auto drawDoubleUpRightShorterLine = [&](quint8 top, quint8 right) { // ╚
+        lightPath.moveTo(origin[top] + dir[right] * doubleLinesDistance);
+        lightPath.lineTo(center + (dir[right] + dir[top]) * doubleLinesDistance);
+        lightPath.lineTo(origin[right] + dir[top] * doubleLinesDistance);
+    };
+    const auto drawUpRight = [&](quint8 top, quint8 right) { // └┗
+        pathForLine(top).moveTo(origin[top]);
+        pathForLine(top).lineTo(center);
+        pathForLine(top).lineTo(origin[right]);
+    };
+
+    switch (basePackedLineTypes) {
+    case makePackedLineTypes(LtHeavy , LtNone  , LtLight , LtNone  ): // ╿ ; ╼ ╽ ╾ ╊ ╇ ╉ ╈ ╀ ┾ ╁ ┽
+        lightPath.moveTo(origin[bottomIndex]);
+        lightPath.lineTo(center + dir[topIndex] * lightLineWidth / 2.0);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+        Q_FALLTHROUGH();
+#endif
+    case makePackedLineTypes(LtHeavy , LtNone  , LtNone  , LtNone  ): // ╹ ; ╺ ╻ ╸ ┻ ┣ ┳ ┫ ┸ ┝ ┰ ┥
+    case makePackedLineTypes(LtLight , LtNone  , LtNone  , LtNone  ): // ╵ ; ╶ ╷ ╴ ┷ ┠ ┯ ┨ ┴ ├ ┬ ┤
+        pathForLine(topIndex).moveTo(origin[topIndex]);
+        pathForLine(topIndex).lineTo(center);
+        break;
+
+    case makePackedLineTypes(LtHeavy , LtHeavy , LtLight , LtLight ): // ╄ ; ╃ ╆ ╅
+        drawUpRight(bottomIndex, leftIndex);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+        Q_FALLTHROUGH();
+#endif
+    case makePackedLineTypes(LtHeavy , LtHeavy , LtNone  , LtNone  ): // ┗ ; ┛ ┏ ┓
+    case makePackedLineTypes(LtLight , LtLight , LtNone  , LtNone  ): // └ ; ┘ ┌ ┐
+        drawUpRight(topIndex, rightIndex);
+        break;
+
+    case makePackedLineTypes(LtHeavy , LtLight , LtNone  , LtNone  ): // ┖ ; ┙ ┍ ┒
+        qSwap(leftIndex, rightIndex);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+        Q_FALLTHROUGH();
+#endif
+    case makePackedLineTypes(LtHeavy , LtNone  , LtNone  , LtLight ): // ┚ ; ┕ ┎ ┑
+        lightPath.moveTo(origin[leftIndex]);
+        lightPath.lineTo(center);
+        heavyPath.moveTo(origin[topIndex]);
+        heavyPath.lineTo(center + dir[bottomIndex] * lightLineWidth / 2.0);
+        break;
+
+    case makePackedLineTypes(LtLight , LtDouble, LtNone  , LtNone  ): // ╘ ; ╜ ╓ ╕
+        qSwap(leftIndex, rightIndex);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+        Q_FALLTHROUGH();
+#endif
+    case makePackedLineTypes(LtLight , LtNone  , LtNone  , LtDouble): // ╛ ; ╙ ╒ ╖
+        lightPath.moveTo(origin[topIndex]);
+        lightPath.lineTo(center + dir[bottomIndex] * doubleLinesDistance);
+        lightPath.lineTo(origin[leftIndex] + dir[bottomIndex] * doubleLinesDistance);
+        lightPath.moveTo(origin[leftIndex] - dir[bottomIndex] * doubleLinesDistance);
+        lightPath.lineTo(center - dir[bottomIndex] * doubleLinesDistance);
+        break;
+
+    case makePackedLineTypes(LtHeavy , LtHeavy , LtLight , LtNone  ): // ┡ ; ┹ ┪ ┲
+        qSwap(leftIndex, bottomIndex);
+        qSwap(rightIndex, topIndex);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+        Q_FALLTHROUGH();
+#endif
+    case makePackedLineTypes(LtHeavy , LtHeavy , LtNone  , LtLight ): // ┺ ; ┩ ┢ ┱
+        drawUpRight(topIndex, rightIndex);
+        lightPath.moveTo(origin[leftIndex]);
+        lightPath.lineTo(center);
+        break;
+
+    case makePackedLineTypes(LtHeavy , LtLight , LtLight , LtNone  ): // ┞ ; ┵ ┧ ┮
+        qSwap(leftIndex, rightIndex);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+        Q_FALLTHROUGH();
+#endif
+    case makePackedLineTypes(LtHeavy , LtNone  , LtLight , LtLight ): // ┦ ; ┶ ┟ ┭
+        heavyPath.moveTo(origin[topIndex]);
+        heavyPath.lineTo(center + dir[bottomIndex] * lightLineWidth / 2.0);
+        drawUpRight(bottomIndex, leftIndex);
+        break;
+
+    case makePackedLineTypes(LtLight , LtDouble, LtNone  , LtDouble): // ╧ ; ╟ ╢ ╤
+        lightPath.moveTo(origin[topIndex]);
+        lightPath.lineTo(center - dir[bottomIndex] * doubleLinesDistance);
+        qSwap(leftIndex, bottomIndex);
+        qSwap(rightIndex, topIndex);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+        Q_FALLTHROUGH();
+#endif
+    case makePackedLineTypes(LtDouble, LtNone  , LtDouble, LtNone  ): // ║ ; ╫ ═ ╪
+        lightPath.moveTo(origin[topIndex] + dir[leftIndex] * doubleLinesDistance);
+        lightPath.lineTo(origin[bottomIndex] + dir[leftIndex] * doubleLinesDistance);
+        lightPath.moveTo(origin[topIndex] + dir[rightIndex] * doubleLinesDistance);
+        lightPath.lineTo(origin[bottomIndex] + dir[rightIndex] * doubleLinesDistance);
+        break;
+
+    case makePackedLineTypes(LtDouble, LtNone  , LtNone  , LtNone  ): // ╨ ; ╞ ╥ ╡
+        lightPath.moveTo(origin[topIndex] + dir[leftIndex] * doubleLinesDistance);
+        lightPath.lineTo(center + dir[leftIndex] * doubleLinesDistance);
+        lightPath.moveTo(origin[topIndex] + dir[rightIndex] * doubleLinesDistance);
+        lightPath.lineTo(center + dir[rightIndex] * doubleLinesDistance);
+        break;
+
+    case makePackedLineTypes(LtDouble, LtDouble, LtDouble, LtDouble): // ╬
+        drawDoubleUpRightShorterLine(topIndex, rightIndex);
+        drawDoubleUpRightShorterLine(bottomIndex, rightIndex);
+        drawDoubleUpRightShorterLine(topIndex, leftIndex);
+        drawDoubleUpRightShorterLine(bottomIndex, leftIndex);
+        break;
+
+    case makePackedLineTypes(LtDouble, LtDouble, LtDouble, LtNone  ): // ╠ ; ╩ ╣ ╦
+        lightPath.moveTo(origin[topIndex] + dir[leftIndex] * doubleLinesDistance);
+        lightPath.lineTo(origin[bottomIndex] + dir[leftIndex] * doubleLinesDistance);
+        drawDoubleUpRightShorterLine(topIndex, rightIndex);
+        drawDoubleUpRightShorterLine(bottomIndex, rightIndex);
+        break;
+
+    case makePackedLineTypes(LtDouble, LtDouble, LtNone  , LtNone  ): // ╚ ; ╝ ╔ ╗
+        lightPath.moveTo(origin[topIndex] + dir[leftIndex] * doubleLinesDistance);
+        lightPath.lineTo(center + (dir[leftIndex] + dir[bottomIndex]) * doubleLinesDistance);
+        lightPath.lineTo(origin[rightIndex] + dir[bottomIndex] * doubleLinesDistance);
+        drawDoubleUpRightShorterLine(topIndex, rightIndex);
+        break;
+    }
+
+    // Draw paths
+    if (!lightPath.isEmpty()) {
+        paint.strokePath(lightPath, lightPen);
+    }
+    if (!heavyPath.isEmpty()) {
+        paint.strokePath(heavyPath, heavyPen);
+    }
+
+    return true;
+}
+
+static inline bool drawDashedLineCharacter(QPainter &paint, int x, int y, int w, int h, uchar code,
+                                           bool bold)
+{
+    if (!((0x04 <= code && code <= 0x0B) || (0x4C <= code && code <= 0x4F))) {
+        return false;
+    }
+
+    const uint lightLineWidth = lineWidth(w, false, bold);
+    const uint heavyLineWidth = lineWidth(w, true, bold);
+
+    const auto lightPen = pen(paint, lightLineWidth);
+    const auto heavyPen = pen(paint, heavyLineWidth);
+
+    // Pixel aligned center point
+    const QPointF center = {
+        int(x + w/2.0) + 0.5 * (lightLineWidth%2),
+        int(y + h/2.0) + 0.5 * (lightLineWidth%2),
+    };
+
+    const qreal halfGapH   = qMax(w / 20.0, 0.5);
+    const qreal halfGapV   = qMax(h / 26.0, 0.5);
+    // For some reason vertical double dash has bigger gap
+    const qreal halfGapDDV = qMax(h / 14.0, 0.5);
+
+    static const int LinesNumMax = 4;
+
+    enum Orientation {Horizontal, Vertical};
+    struct {
+        int         linesNum;
+        Orientation orientation;
+        QPen        pen;
+        qreal       halfGap;
+    } lineProps;
+
+    switch (code) {
+    case 0x4C: lineProps = {2, Horizontal, lightPen, halfGapH  }; break; // ╌
+    case 0x4D: lineProps = {2, Horizontal, heavyPen, halfGapH  }; break; // ╍
+    case 0x4E: lineProps = {2, Vertical  , lightPen, halfGapDDV}; break; // ╎
+    case 0x4F: lineProps = {2, Vertical  , heavyPen, halfGapDDV}; break; // ╏
+    case 0x04: lineProps = {3, Horizontal, lightPen, halfGapH  }; break; // ┄
+    case 0x05: lineProps = {3, Horizontal, heavyPen, halfGapH  }; break; // ┅
+    case 0x06: lineProps = {3, Vertical  , lightPen, halfGapV  }; break; // ┆
+    case 0x07: lineProps = {3, Vertical  , heavyPen, halfGapV  }; break; // ┇
+    case 0x08: lineProps = {4, Horizontal, lightPen, halfGapH  }; break; // ┈
+    case 0x09: lineProps = {4, Horizontal, heavyPen, halfGapH  }; break; // ┉
+    case 0x0A: lineProps = {4, Vertical  , lightPen, halfGapV  }; break; // ┊
+    case 0x0B: lineProps = {4, Vertical  , heavyPen, halfGapV  }; break; // ┋
+    }
+
+    Q_ASSERT(lineProps.linesNum <= LinesNumMax);
+    const int size = (lineProps.orientation == Horizontal ? w : h);
+    const int pos  = (lineProps.orientation == Horizontal ? x : y);
+    QLineF lines[LinesNumMax];
+
+    for (int i = 0; i < lineProps.linesNum; i++) {
+        const qreal start = pos + qreal(size * (i  )) / lineProps.linesNum;
+        const qreal end   = pos + qreal(size * (i+1)) / lineProps.linesNum;
+        if (lineProps.orientation == Horizontal) {
+            lines[i] = QLineF{start + lineProps.halfGap, center.y(),
+                              end   - lineProps.halfGap, center.y()};
+        } else {
+            lines[i] = QLineF{center.x(), start + lineProps.halfGap,
+                              center.x(), end   - lineProps.halfGap};
+        }
+    }
+
+    const auto origPen = paint.pen();
+
+    paint.setPen(lineProps.pen);
+    paint.drawLines(lines, lineProps.linesNum);
+
+    paint.setPen(origPen);
+    return true;
+}
+
+static inline bool drawRoundedCornerLineCharacter(QPainter &paint, int x, int y, int w, int h,
+                                                  uchar code, bool bold)
+{
+    if (!(0x6D <= code && code <= 0x70)) {
+        return false;
+    }
+
+    const uint lightLineWidth = lineWidth(w, false, bold);
+    const auto lightPen = pen(paint, lightLineWidth);
+
+    // Pixel aligned center point
+    const QPointF center = {
+        int(x + w/2.0) + 0.5 * (lightLineWidth%2),
+        int(y + h/2.0) + 0.5 * (lightLineWidth%2),
+    };
+
+    const int r = w * 3 / 8;
+    const int d = 2 * r;
+
+    QPainterPath path;
+
+    // lineTo() between moveTo and arcTo is redundant - arcTo() draws line
+    // to the arc's starting point
+    switch (code) {
+    case 0x6D: // BOX DRAWINGS LIGHT ARC DOWN AND RIGHT
+        path.moveTo(center.x(), y + h);
+        path.arcTo(center.x(), center.y(), d, d, 180, -90);
+        path.lineTo(x + w, center.y());
+        break;
+    case 0x6E: // BOX DRAWINGS LIGHT ARC DOWN AND LEFT
+        path.moveTo(center.x(), y + h);
+        path.arcTo(center.x() - d, center.y(), d, d, 0, 90);
+        path.lineTo(x, center.y());
+        break;
+    case 0x6F: // BOX DRAWINGS LIGHT ARC UP AND LEFT
+        path.moveTo(center.x(), y);
+        path.arcTo(center.x() - d, center.y() - d, d, d, 0, -90);
+        path.lineTo(x, center.y());
+        break;
+    case 0x70: // BOX DRAWINGS LIGHT ARC UP AND RIGHT
+        path.moveTo(center.x(), y);
+        path.arcTo(center.x(), center.y() - d, d, d, 180, 90);
+        path.lineTo(x + w, center.y());
+        break;
+    }
+    paint.strokePath(path, lightPen);
+
+    return true;
+}
+
+static inline bool drawDiagonalLineCharacter(QPainter &paint, int x, int y, int w, int h,
+                                             uchar code, bool bold)
+{
+    if (!(0x71 <= code && code <= 0x73)) {
+        return false;
+    }
+
+    const uint lightLineWidth = lineWidth(w, false, bold);
+    const auto lightPen = pen(paint, lightLineWidth);
+
+    const QLineF lines[] = {
+        QLineF(x+w, y, x  , y+h), // '/'
+        QLineF(x  , y, x+w, y+h), // '\'
+    };
+
+    const auto origPen = paint.pen();
+
+    paint.setPen(lightPen);
+    switch (code) {
+    case 0x71: // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
+        paint.drawLine(lines[0]);
+        break;
+    case 0x72: // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT
+        paint.drawLine(lines[1]);
+        break;
+    case 0x73: // BOX DRAWINGS LIGHT DIAGONAL CROSS
+        paint.drawLines(lines, 2);
+        break;
+    }
+
+    paint.setPen(origPen);
+    return true;
+}
+
+static inline bool drawBlockCharacter(QPainter &paint, int x, int y, int w, int h, uchar code,
+                                      bool bold)
+{
+    Q_UNUSED(bold);
+
+    const QColor color = paint.pen().color();
+
+    // Center point
+    const QPointF center = {
+        x + w/2.0,
+        y + h/2.0,
+    };
+
+    // Default rect fills entire cell
+    QRectF rect(x, y, w, h);
+
+    // LOWER ONE EIGHTH BLOCK to LEFT ONE EIGHTH BLOCK
+    if (code >= 0x81 && code <= 0x8f) {
+        if (code < 0x88) { // Horizontal
+            const qreal height = h * (0x88 - code) / 8.0;
+            rect.setY(y + height);
+            rect.setHeight(h - height);
+        } else if (code > 0x88) { // Vertical
+            const qreal width = w * (0x90 - code) / 8.0;
+            rect.setWidth(width);
+        }
+        paint.fillRect(rect, color);
+
+        return true;
+    }
+
+    // Combinations of quarter squares
+    // LEFT ONE EIGHTH BLOCK to QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT
+    if (code >= 0x96 && code <= 0x9F) {
+        const QRectF upperLeft (x         , y         , w/2.0, h/2.0);
+        const QRectF upperRight(center.x(), y         , w/2.0, h/2.0);
+        const QRectF lowerLeft (x         , center.y(), w/2.0, h/2.0);
+        const QRectF lowerRight(center.x(), center.y(), w/2.0, h/2.0);
+
+        QPainterPath path;
+
+        switch (code) {
+        case 0x96: // ▖
+            path.addRect(lowerLeft);
+            break;
+        case 0x97: // ▗
+            path.addRect(lowerRight);
+            break;
+        case 0x98: // ▘
+            path.addRect(upperLeft);
+            break;
+        case 0x99: // ▙
+            path.addRect(upperLeft);
+            path.addRect(lowerLeft);
+            path.addRect(lowerRight);
+            break;
+        case 0x9a: // ▚
+            path.addRect(upperLeft);
+            path.addRect(lowerRight);
+            break;
+        case 0x9b: // ▛
+            path.addRect(upperLeft);
+            path.addRect(upperRight);
+            path.addRect(lowerLeft);
+            break;
+        case 0x9c: // ▜
+            path.addRect(upperLeft);
+            path.addRect(upperRight);
+            path.addRect(lowerRight);
+            break;
+        case 0x9d: // ▝
+            path.addRect(upperRight);
+            break;
+        case 0x9e: // ▞
+            path.addRect(upperRight);
+            path.addRect(lowerLeft);
+            break;
+        case 0x9f: // ▟
+            path.addRect(upperRight);
+            path.addRect(lowerLeft);
+            path.addRect(lowerRight);
+            break;
+        }
+        paint.fillPath(path, color);
+
+        return true;
+    }
+
+    QBrush lightShade, mediumShade, darkShade;
+    if (paint.testRenderHint(QPainter::Antialiasing)) {
+        lightShade  = QColor(color.red(), color.green(), color.blue(), 64);
+        mediumShade = QColor(color.red(), color.green(), color.blue(), 128);
+        darkShade   = QColor(color.red(), color.green(), color.blue(), 192);
+    } else {
+        lightShade  = QBrush(color, Qt::Dense6Pattern);
+        mediumShade = QBrush(color, Qt::Dense4Pattern);
+        darkShade   = QBrush(color, Qt::Dense2Pattern);
+    }
+    // And the random stuff
+    switch (code) {
+    case 0x80: // Top half block
+        rect.setHeight(h/2.0);
+        paint.fillRect(rect, color);
+        return true;
+    case 0x90: // Right half block
+        rect.moveLeft(center.x());
+        paint.fillRect(rect, color);
+        return true;
+    case 0x94: // Top one eighth block
+        rect.setHeight(h/8.0);
+        paint.fillRect(rect, color);
+        return true;
+    case 0x95: { // Right one eighth block
+            const qreal width = 7 * w / 8.0;
+            rect.moveLeft(x + width);
+            paint.fillRect(rect, color);
+            return true;
+        }
+    case 0x91: // Light shade
+        paint.fillRect(rect, lightShade);
+        return true;
+    case 0x92: // Medium shade
+        paint.fillRect(rect, mediumShade);
+        return true;
+    case 0x93: // Dark shade
+        paint.fillRect(rect, darkShade);
+        return true;
+
+    default:
+        return false;
+    }
+}
+
+void draw(QPainter &paint, const QRect &cellRect, const QChar &chr, bool bold)
+{
+    static const ushort FirstBoxDrawingCharacterCodePoint = 0x2500;
+    const uchar code = chr.unicode() - FirstBoxDrawingCharacterCodePoint;
+
+    int x = cellRect.x();
+    int y = cellRect.y();
+    int w = cellRect.width();
+    int h = cellRect.height();
+
+    // Each function below returns true when it has drawn the character, false otherwise.
+    drawBasicLineCharacter(paint, x, y, w, h, code, bold)
+            || drawDashedLineCharacter(paint, x, y, w, h, code, bold)
+            || drawRoundedCornerLineCharacter(paint, x, y, w, h, code, bold)
+            || drawDiagonalLineCharacter(paint, x, y, w, h, code, bold)
+            || drawBlockCharacter(paint, x, y, w, h, code, bold);
+}
+
+} // namespace LineBlockCharacters
+} // namespace Konsole
diff --git a/src/LineBlockCharacters.h b/src/LineBlockCharacters.h
new file mode 100644
index 0000000..a29f057
--- /dev/null
+++ b/src/LineBlockCharacters.h
@@ -0,0 +1,57 @@
+/*
+    This file is part of Konsole, a terminal emulator for KDE.
+
+    Copyright 2019 by Mariusz Glebocki <mglb@arccos-1.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+    02110-1301  USA.
+*/
+
+#ifndef LINEBLOCKCHARACTERS_H
+#define LINEBLOCKCHARACTERS_H
+
+// Qt
+#include <QPainter>
+#include <QtGlobal>
+
+namespace Konsole {
+
+/**
+ * Helper functions for drawing characters from "Box Drawing" and "Block Elements" Unicode blocks.
+ */
+namespace LineBlockCharacters {
+
+    /**
+     * Returns true if the character can be drawn by draw() function.
+     *
+     * @param ucs4cp Character to test's UCS4 code point
+     */
+    inline static bool canDraw(uint ucs4cp) {
+        return (0x2500 <= ucs4cp && ucs4cp <= 0x259F);
+    }
+
+    /**
+     * Draws character.
+     *
+     * @param paint QPainter to draw on
+     * @param cellRect Rectangle to draw in
+     * @param chr Character to be drawn
+     */
+    void draw(QPainter &paint, const QRect &cellRect, const QChar &chr, bool bold);
+
+} // namespace LineBlockCharacters
+} // namespace Konsole
+
+#endif // LINEBLOCKCHARACTERS_H
diff --git a/src/LineFont.h b/src/LineFont.h
deleted file mode 100644
index 5f81754..0000000
--- a/src/LineFont.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// WARNING: Autogenerated by "fontembedder /Volumes/Projects/KDE/src/kde/applications/konsole/src/LineFont.src".
-// You probably do not want to hand-edit this!
-
-static const quint32 LineChars[] = {
-  0x00007c00, 0x000fffe0, 0x00421084, 0x00e739ce, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
-  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00427000, 0x004e7380, 0x00e77800, 0x00ef7bc0,
-  0x00421c00, 0x00439ce0, 0x00e73c00, 0x00e7bde0, 0x00007084, 0x000e7384, 0x000079ce, 0x000f7bce,
-  0x00001c84, 0x00039ce4, 0x00003dce, 0x0007bdee, 0x00427084, 0x004e7384, 0x004279ce, 0x00e77884,
-  0x00e779ce, 0x004f7bce, 0x00ef7bc4, 0x00ef7bce, 0x00421c84, 0x00439ce4, 0x00423dce, 0x00e73c84,
-  0x00e73dce, 0x0047bdee, 0x00e7bde4, 0x00e7bdee, 0x00427c00, 0x0043fce0, 0x004e7f80, 0x004fffe0,
-  0x00e77c00, 0x00e7fde0, 0x00ef7fc0, 0x00efffe0, 0x00007c84, 0x0003fce4, 0x000e7f84, 0x000fffe4,
-  0x00007dce, 0x0007fdee, 0x000f7fce, 0x000fffee, 0x00427c84, 0x0043fce4, 0x004e7f84, 0x004fffe4,
-  0x00427dce, 0x00e77c84, 0x00e77dce, 0x0047fdee, 0x004f7fce, 0x00e7fde4, 0x00ef7fc4, 0x004fffee,
-  0x00efffe4, 0x00e7fdee, 0x00ef7fce, 0x00efffee, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
-  0x000f83e0, 0x00a5294a, 0x004e1380, 0x00a57800, 0x00ad0bc0, 0x004390e0, 0x00a53c00, 0x00a5a1e0,
-  0x000e1384, 0x0000794a, 0x000f0b4a, 0x000390e4, 0x00003d4a, 0x0007a16a, 0x004e1384, 0x00a5694a,
-  0x00ad0b4a, 0x004390e4, 0x00a52d4a, 0x00a5a16a, 0x004f83e0, 0x00a57c00, 0x00ad83e0, 0x000f83e4,
-  0x00007d4a, 0x000f836a, 0x004f93e4, 0x00a57d4a, 0x00ad836a, 0x00000000, 0x00000000, 0x00000000,
-  0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00001c00, 0x00001084, 0x00007000, 0x00421000,
-  0x00039ce0, 0x000039ce, 0x000e7380, 0x00e73800, 0x000e7f80, 0x00e73884, 0x0003fce0, 0x004239ce
-};
diff --git a/src/LineFont.src b/src/LineFont.src
deleted file mode 100644
index 1b5967d..0000000
--- a/src/LineFont.src
+++ /dev/null
@@ -1,786 +0,0 @@
-#2500: single horizontal line
-2500
-     
-     
------
-     
-     
-
-#2501: triple horizontal line
-2501
-     
------
------
------
-     
-
-#2502: single vertical line
-2502
-  |  
-  |  
-  |  
-  |  
-  |  
-
-#2503: triple vertical line
-2503
- ||| 
- ||| 
- ||| 
- ||| 
- ||| 
-
-#2504-250B are dashed - not handled
-
-#250C: top-left corner (lines on bottom + right)
-250C
-     
-     
-  .--
-  |  
-  |  
-
-#250D: as above, but top line triple-width
-250D
-     
-  .--
-  .--
-  |--
-  |  
-
-#250E: now the vert line triple-width
-250E
-     
-     
- ..--
- ||| 
- ||| 
-
-#250F: and now both lines triple-width
-250F
-     
- .___
- |.--
- ||._
- ||| 
-
-#2510: top-right corner
-2510
-     
-     
---.  
-  |  
-  |  
-
-2511
-     
-==.  
-==.  
-==|  
-  | 
-
-2512
-     
-     
-==.. 
- ||| 
- ||| 
-
-2513
-     
-===. 
-==.| 
-=.|| 
- ||| 
-
-#2514: bottom-left corner
-2514
-  |  
-  |  
-  .==
-     
-     
-
-2515
-  |  
-  |==
-  |==
-  ===
-     
-
-
-2516
- ||| 
- ||| 
- |.==
-     
-     
-
-2517
- ||| 
- ||.=
- |.==
- .===
-     
-
-#2518: bottm-right corner
-2518
-  |  
-  |  
-==.  
-     
-     
-
-2519
-  |  
-==|  
-==|  
-===  
-     
-
-
-251A
- ||| 
- ||| 
-==== 
-     
-     
-
-251B
- ||| 
-=.|| 
-==.| 
-===. 
-     
-
-#251C: Join of vertical line and one from the right
-251C
-  |  
-  |  
-  |==
-  |  
-  |  
-
-251D
-  |  
-  |==
-  |==
-  |==
-  |  
-
-251E
- ||| 
- ||| 
- ||==
-  |  
-  |  
-
-251F
-  |  
-  |  
- ||==
- ||| 
- ||| 
-
-
-2520
- ||| 
- ||| 
- ||==
- ||| 
- ||| 
-
-2521
- ||| 
- |||=
- ||==
- .|==
-  |  
-
-2522
-  |  
- .|==
- ||==
- |||=
- ||| 
-
-2523
- ||| 
- ||.=
- ||==
- ||.=
- ||| 
-
-#2524: Join of vertical line and one from the left
-2524
-  |  
-  |  
-==|  
-  |  
-  |  
-
-2525
-  |  
-==|  
-==|  
-==|  
-  |  
-
-2526
- ||| 
- ||| 
-==+| 
-  |  
-  |  
-
-2527
-  |  
-  |  
-==+| 
- ||| 
- ||| 
-
-2528
- ||| 
- ||| 
-==+| 
- ||| 
- ||| 
-
-2529
- ||| 
-=+|| 
-==+| 
-===+ 
-  |  
-
-252A
-  |  
-=+|| 
-==+| 
-===+ 
- ||| 
-
-252B
- |||
-=+|| 
-==+| 
-=+|| 
- |||
-
-#252C: horizontal line joined to from below
-252C
-     
-     
-=====
-  |  
-  |  
-
-252D
-     
-===  
-==|==
-==|  
-  |  
-
-252E
-     
-  ===
-==|==
-  |==
-  |  
-
-252F
-     
-==+==
-==|==
-==|==
-  |  
-
-2530
-     
-     
-=====
- ||| 
- ||| 
-
-2531
-     
-===| 
-==||=
-=||| 
- ||| 
-
-2532
-     
- |===
-=||==
- ||==
- ||| 
-
-2533
-     
-=====
-==|==
-=+|+=
- ||| 
-
-#2534: bottom line, connected to from top
-2534
-  |
-  |
-=====
-     
-     
-
-2535
-  |
-==|
-=====
-===  
-    
-
-2536
-  |
-  |==
-=====
-  ===
-     
-
-2537
-  |
-==|==
-=====
-=====
-     
-
-2538
- |||
- |||
-=====
-     
-     
-
-2539
- |||
-==||
-=====
-===| 
-    
-
-
-253A
- |||
- ||==
-=|===
- |===
-     
-
-253B
- |||
-==|==
-=====
-=====
-     
-
-#253C: vertical + horizontal lines intersecting
-253C
-  |  
-  |  
-=====
-  |  
-  |
-
-253D
-  |  
-==|  
-=====
-==|  
-  |
-
-253E
-  |  
-  |==
-=====
-  |==
-  |
-
-253F
-  |  
-==|==
-=====
-==|==
-  |
-
-2540
- ||| 
- ||| 
-=====
-  |  
-  |
-
-2541
-  |  
-  |  
-=====
- ||| 
- |||
-
-2542
- ||| 
- ||| 
-=====
- ||| 
- |||
-
-2543
- ||| 
-=|||
-=====
-==|+ 
-  |
-
-2544
- ||| 
- ||==
-=====
- ||==
-  |
-
-2545
-  |
-==|+ 
-=====
-=|||
- ||| 
-
-2546
-  |
- ||==
-=====
- ||==
- ||| 
-
-2547
- ||| 
-=|||=
-=====
-=|||=
-  | 
-
-2548
-  |  
-=|||=
-=====
-=|||=
- |||
-
-2549
- ||| 
-=||| 
-=====
-=||| 
- |||
-
-254A
- ||| 
- |||=
-=====
- |||=
- |||
-
-254B
- ||| 
-=|||=
-=====
-=|||=
- |||
-
-#254C-254F are dashed
-2550
-     
-_____
-     
-_____
-     
-
-2551
- | | 
- | |
- | |
- | |
- | |
-
-2552
-     
-  |--
-  |
-  |--
-  |
-
-2553
-     
-     
- ----
- | | 
- | | 
-
-2554
-     
- +---
- |
- + +-
- | |
-
-2555
-     
---+
-  |  
---+  
-  |  
-
-2556
-    
-    
--+-+
- | |
- | |
-
-2557
-     
----+ 
-   | 
--+ |
- | |
-
-2558
-  |
-  +--
-  |
-  +--
-
-2559
- | | 
- | | 
- +-+-
-     
-     
-
-255A
- | | 
- | +-
- |   
- +---
-     
-
-255B
-  |  
---+  
-  | 
---+  
-     
-
-255C
- | | 
- | | 
--+-+ 
-    
-
-255D
- | | 
--+ | 
-   |
----+
-    
-
-255E
-  |
-  +--
-  |
-  +--
-  |
-
-255F
- | |
- | |
- | +-
- | |
- | |
-
-2560
- | |
- | +-
- |  
- | +-
- | |
-
-2561
-  |
---+
-  |
---+
-  |
-
-2562
- | | 
- | |
--+ +
- | |
- | |
-
-2563
- | |
--+ |
-   |
--+ |
- | |
-
-2564
-     
------
-     
---+--
-  |
-
-2565
-     
-     
--+-+-
- | | 
- | |
-
-2566
-     
------
-     
--+ +-
- | |
-
-2567
-  |  
---+--
-     
------
-     
-
-2568
- | | 
- | | 
--+-+-
-     
-     
-
-2569
- | | 
--+ +-
-     
------
-     
-
-256A
-  |  
---+--
-  |  
---+--
-  |
-
-256B
- | | 
- | | 
--+-+-
- | | 
- | | 
-
-256C
- | | 
--+ +-
-
--+ +-
- | | 
-
-#256F-2570 are curly,
-#2571-2573 are slashes and X
-
-2574
-     
-     
-___  
-     
-     
-
-2575
-  |  
-  |  
-  |  
-     
-    
-
-2576
-     
-     
-  ___
-     
-     
-
-2577
-     
-    
-  |  
-  |  
-  |  
-
-2578
-     
-___  
-___  
-___  
-     
-
-2579
- ||| 
- ||| 
- ||| 
-     
-    
-
-257A
-     
-  ___
-  ___
-  ___
-     
-
-257B
-     
-    
- ||| 
- ||| 
- ||| 
-
-257C
-     
-  ___
-_____
-  ___
-     
-
-257D
-  |  
-  |  
- ||| 
- ||| 
- ||| 
-
-257E
-     
-___  
-_____
-___  
-     
-
-257F
- ||| 
- ||| 
- ||| 
-  |  
-  |  
diff --git a/src/TerminalDisplay.cpp b/src/TerminalDisplay.cpp
index 3169a50..140ecfb 100644
--- a/src/TerminalDisplay.cpp
+++ b/src/TerminalDisplay.cpp
@@ -45,6 +45,7 @@
 #include <QDrag>
 #include <QDesktopServices>
 #include <QAccessible>
+#include <QtMath>
 
 // KDE
 #include <KShell>
@@ -63,7 +64,6 @@
 #include "konsoledebug.h"
 #include "TerminalCharacterDecoder.h"
 #include "Screen.h"
-#include "LineFont.h"
 #include "SessionController.h"
 #include "ExtendedCharTable.h"
 #include "TerminalDisplayAccessible.h"
@@ -71,6 +71,7 @@
 #include "Session.h"
 #include "WindowSystemInfo.h"
 #include "IncrementalSearchBar.h"
+#include "LineBlockCharacters.h"
 
 using namespace Konsole;
 
@@ -211,7 +212,7 @@ static inline bool isLineCharString(const QString& string)
         return false;
     }
 
-    return isSupportedLineChar(string.at(0).unicode());
+    return LineBlockCharacters::canDraw(string.at(0).unicode());
 }
 
 void TerminalDisplay::fontChange(const QFont&)
@@ -566,386 +567,16 @@ TerminalDisplay::~TerminalDisplay()
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
-/**
- A table for emulating the simple (single width) unicode drawing chars.
- It represents the 250x - 257x glyphs. If it's zero, we can't use it.
- if it's not, it's encoded as follows: imagine a 5x5 grid where the points are numbered
- 0 to 24 left to top, top to bottom. Each point is represented by the corresponding bit.
-
- Then, the pixels basically have the following interpretation:
- _|||_
- -...-
- -...-
- -...-
- _|||_
-
-where _ = none
-      | = vertical line.
-      - = horizontal line.
- */
-
-enum LineEncode {
-    TopL  = (1 << 1),
-    TopC  = (1 << 2),
-    TopR  = (1 << 3),
-
-    LeftT = (1 << 5),
-    Int11 = (1 << 6),
-    Int12 = (1 << 7),
-    Int13 = (1 << 8),
-    RightT = (1 << 9),
-
-    LeftC = (1 << 10),
-    Int21 = (1 << 11),
-    Int22 = (1 << 12),
-    Int23 = (1 << 13),
-    RightC = (1 << 14),
-
-    LeftB = (1 << 15),
-    Int31 = (1 << 16),
-    Int32 = (1 << 17),
-    Int33 = (1 << 18),
-    RightB = (1 << 19),
-
-    BotL  = (1 << 21),
-    BotC  = (1 << 22),
-    BotR  = (1 << 23)
-};
-
-static void drawLineChar(QPainter& paint, int x, int y, int w, int h, uchar code)
-{
-    //Calculate cell midpoints, end points.
-    const int cx = x + w / 2;
-    const int cy = y + h / 2. - 0.5;
-    const int ex = x + w - 1;
-    const int ey = y + h - 1;
-
-    const quint32 toDraw = LineChars[code];
-
-    //Top _lines:
-    if ((toDraw & TopL) != 0u) {
-        paint.drawLine(cx - 1, y, cx - 1, cy - 2);
-    }
-    if ((toDraw & TopC) != 0u) {
-        paint.drawLine(cx, y, cx, cy - 2);
-    }
-    if ((toDraw & TopR) != 0u) {
-        paint.drawLine(cx + 1, y, cx + 1, cy - 2);
-    }
-
-    //Bot _lines:
-    if ((toDraw & BotL) != 0u) {
-        paint.drawLine(cx - 1, cy + 2, cx - 1, ey);
-    }
-    if ((toDraw & BotC) != 0u) {
-        paint.drawLine(cx, cy + 2, cx, ey);
-    }
-    if ((toDraw & BotR) != 0u) {
-        paint.drawLine(cx + 1, cy + 2, cx + 1, ey);
-    }
-
-    //Left _lines:
-    if ((toDraw & LeftT) != 0u) {
-        paint.drawLine(x, cy - 1, cx - 2, cy - 1);
-    }
-    if ((toDraw & LeftC) != 0u) {
-        paint.drawLine(x, cy, cx - 2, cy);
-    }
-    if ((toDraw & LeftB) != 0u) {
-        paint.drawLine(x, cy + 1, cx - 2, cy + 1);
-    }
-
-    //Right _lines:
-    if ((toDraw & RightT) != 0u) {
-        paint.drawLine(cx + 2, cy - 1, ex, cy - 1);
-    }
-    if ((toDraw & RightC) != 0u) {
-        paint.drawLine(cx + 2, cy, ex, cy);
-    }
-    if ((toDraw & RightB) != 0u) {
-        paint.drawLine(cx + 2, cy + 1, ex, cy + 1);
-    }
-
-    //Intersection points.
-    if ((toDraw & Int11) != 0u) {
-        paint.drawPoint(cx - 2, cy - 2);
-    }
-    if ((toDraw & Int12) != 0u) {
-        paint.drawPoint(cx - 1, cy - 2);
-    }
-    if ((toDraw & Int13) != 0u) {
-        paint.drawPoint(cx - 0, cy - 2);
-    }
-
-    if ((toDraw & Int21) != 0u) {
-        paint.drawPoint(cx - 2, cy - 1);
-    }
-    if ((toDraw & Int22) != 0u) {
-        paint.drawPoint(cx - 1, cy - 1);
-    }
-    if ((toDraw & Int23) != 0u) {
-        paint.drawPoint(cx - 0, cy - 1);
-    }
-
-    if ((toDraw & Int31) != 0u) {
-        paint.drawPoint(cx - 2, cy);
-    }
-    if ((toDraw & Int32) != 0u) {
-        paint.drawPoint(cx - 1, cy);
-    }
-    if ((toDraw & Int33) != 0u) {
-        paint.drawPoint(cx - 0, cy);
-    }
-}
-
-static void drawOtherChar(QPainter& paint, int x, int y, int w, int h, uchar code)
-{
-    //Calculate cell midpoints, end points.
-    const int cx = x + w / 2;
-    const int cy = y + h / 2. - 0.5; // Compensate for the translation, to match fonts
-    const int ex = x + w - 1;
-    const int ey = y + h - 1;
-
-    // Double dashes
-    if (0x4C <= code && code <= 0x4F) {
-        const int xHalfGap = qMax(w / 15, 1);
-        const int yHalfGap = qMax(h / 15, 1);
-        switch (code) {
-        case 0x4D: // BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL
-            paint.drawLine(x, cy - 1, cx - xHalfGap - 1, cy - 1);
-            paint.drawLine(x, cy + 1, cx - xHalfGap - 1, cy + 1);
-            paint.drawLine(cx + xHalfGap, cy - 1, ex, cy - 1);
-            paint.drawLine(cx + xHalfGap, cy + 1, ex, cy + 1);
-            // No break!
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
-            Q_FALLTHROUGH();
-#endif
-        case 0x4C: // BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL
-            paint.drawLine(x, cy, cx - xHalfGap - 1, cy);
-            paint.drawLine(cx + xHalfGap, cy, ex, cy);
-            break;
-        case 0x4F: // BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL
-            paint.drawLine(cx - 1, y, cx - 1, cy - yHalfGap - 1);
-            paint.drawLine(cx + 1, y, cx + 1, cy - yHalfGap - 1);
-            paint.drawLine(cx - 1, cy + yHalfGap, cx - 1, ey);
-            paint.drawLine(cx + 1, cy + yHalfGap, cx + 1, ey);
-            // No break!
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
-            Q_FALLTHROUGH();
-#endif
-        case 0x4E: // BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL
-            paint.drawLine(cx, y, cx, cy - yHalfGap - 1);
-            paint.drawLine(cx, cy + yHalfGap, cx, ey);
-            break;
-        }
-    }
-
-    // Rounded corner characters
-    else if (0x6D <= code && code <= 0x70) {
-        const int r = w * 3 / 8;
-        const int d = 2 * r;
-        switch (code) {
-        case 0x6D: // BOX DRAWINGS LIGHT ARC DOWN AND RIGHT
-            paint.drawLine(cx, cy + r, cx, ey);
-            paint.drawLine(cx + r, cy, ex, cy);
-            paint.drawArc(cx, cy, d, d, 90 * 16, 90 * 16);
-            break;
-        case 0x6E: // BOX DRAWINGS LIGHT ARC DOWN AND LEFT
-            paint.drawLine(cx, cy + r, cx, ey);
-            paint.drawLine(x, cy, cx - r, cy);
-            paint.drawArc(cx - d, cy, d, d, 0 * 16, 90 * 16);
-            break;
-        case 0x6F: // BOX DRAWINGS LIGHT ARC UP AND LEFT
-            paint.drawLine(cx, y, cx, cy - r);
-            paint.drawLine(x, cy, cx - r, cy);
-            paint.drawArc(cx - d, cy - d, d, d, 270 * 16, 90 * 16);
-            break;
-        case 0x70: // BOX DRAWINGS LIGHT ARC UP AND RIGHT
-            paint.drawLine(cx, y, cx, cy - r);
-            paint.drawLine(cx + r, cy, ex, cy);
-            paint.drawArc(cx, cy - d, d, d, 180 * 16, 90 * 16);
-            break;
-        }
-    }
-
-    // Diagonals
-    else if (0x71 <= code && code <= 0x73) {
-        switch (code) {
-        case 0x71: // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
-            paint.drawLine(ex, y, x, ey);
-            break;
-        case 0x72: // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT
-            paint.drawLine(x, y, ex, ey);
-            break;
-        case 0x73: // BOX DRAWINGS LIGHT DIAGONAL CROSS
-            paint.drawLine(ex, y, x, ey);
-            paint.drawLine(x, y, ex, ey);
-            break;
-        }
-    }
-}
-
-static void drawBlockChar(QPainter& paint, int x, int y, int w, int h, uchar code)
-{
-    const QColor color = paint.pen().color();
-
-    const float left = x - 0.5;
-    const float top = y - 0.5;
-
-    const float cx = left + w / 2;
-    const float cy = top + h / 2;
-    const float right = x + w - 0.5;
-    const float bottom = y + h - 0.5;
-
-    // Default rect fills entire cell
-    QRectF rect(left, top, w, h);
-
-    // LOWER ONE EIGHTH BLOCK to LEFT ONE EIGHTH BLOCK
-    if (code >= 0x81 && code <= 0x8f) {
-        if (code < 0x88) { // Horizontal
-            const int height = h * (0x88 - code) / 8;
-            rect.setY(top + height);
-            rect.setHeight(h - height);
-        } else if (code > 0x88) { // Vertical
-            const int width = w * (0x90 - code) / 8;
-            rect.setWidth(width);
-        }
-
-        paint.fillRect(rect, color);
-
-        return;
-    }
-
-    // Combinations of quarter squares
-    // LEFT ONE EIGHTH BLOCK to QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT
-    if (code >= 0x96 && code <= 0x9F) {
-        bool upperLeft = false, upperRight = false,
-             lowerLeft = false, lowerRight = false;
-
-        switch(code) {
-        case 0x96:
-            lowerLeft = true;
-            break;
-        case 0x97:
-            lowerRight = true;
-            break;
-        case 0x98:
-            upperLeft = true;
-            break;
-        case 0x99:
-            upperLeft = true;
-            lowerLeft = true;
-            lowerRight = true;
-            break;
-        case 0x9a:
-            upperLeft = true;
-            lowerRight = true;
-            break;
-        case 0x9b:
-            upperLeft = true;
-            upperRight = true;
-            lowerLeft = true;
-            break;
-        case 0x9c:
-            upperLeft = true;
-            upperRight = true;
-            lowerRight = true;
-            break;
-        case 0x9d:
-            upperRight = true;
-            break;
-        case 0x9e:
-            upperRight = true;
-            lowerLeft = true;
-            break;
-        case 0x9f:
-            upperRight = true;
-            lowerLeft = true;
-            lowerRight = true;
-            break;
-        default:
-            break;
-        }
-
-        if (upperLeft) {
-            paint.fillRect(QRectF(QPointF(left, top), QPointF(cx, cy)), color);
-        }
-        if (upperRight) {
-            paint.fillRect(QRectF(QPointF(cx, top), QPointF(right, cy)), color);
-        }
-        if (lowerLeft) {
-            paint.fillRect(QRectF(QPointF(left, cy), QPointF(cx, bottom)), color);
-        }
-        if (lowerRight) {
-            paint.fillRect(QRectF(QPointF(cx, cy), QPointF(right, bottom)), color);
-        }
-
-        return;
-    }
-
-    // And the random stuff
-    switch(code) {
-    case 0x80: // Top half block
-        rect.setHeight(h / 2);
-        paint.fillRect(rect, color);
-        return;
-    case 0x90: // Right half block
-        paint.fillRect(QRectF(QPointF(cx, top), QPointF(right, bottom)), color);
-        return;
-    case 0x94: // Top one eighth block
-        rect.setHeight(h / 8);
-        paint.fillRect(rect, color);
-        return;
-    case 0x95: { // Right one eighth block
-        const float width = 7 * w / 8;
-        rect.setX(left + width);
-        rect.setWidth(w - width);
-        paint.fillRect(rect, color);
-        return;
-    }
-    case 0x91: // Light shade
-        paint.fillRect(rect, QBrush(color, Qt::Dense6Pattern));
-        return;
-    case 0x92: // Medium shade
-        paint.fillRect(rect, QBrush(color, Qt::Dense4Pattern));
-        return;
-    case 0x93: // Dark shade
-        paint.fillRect(rect, QBrush(color, Qt::Dense2Pattern));
-        return;
-
-    default:
-        break;
-    }
-}
-
 void TerminalDisplay::drawLineCharString(QPainter& painter, int x, int y, const QString& str,
         const Character* attributes)
 {
-    painter.save();
-
-    // For antialiasing, we need to shift it so the single pixel width is in the middle
-    painter.translate(0.5, 0.5);
-
-    if (((attributes->rendition & RE_BOLD) != 0) && _boldIntense) {
-        QPen boldPen(painter.pen());
-        boldPen.setWidth(4);
-        painter.setPen(boldPen);
-    }
-
+    const bool useBoldPen = (attributes->rendition & RE_BOLD) != 0 && _boldIntense;
 
+    QRect cellRect = {x, y, _fontWidth, _fontHeight};
     for (int i = 0 ; i < str.length(); i++) {
-        const uchar code = str[i].cell();
-
-        if (code >= 0x80 && code <= 0x9F) { // UPPER HALF BLOCK to QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT
-            drawBlockChar(painter, x + (_fontWidth * i), y, _fontWidth, _fontHeight, code);
-        } else if (LineChars[code] != 0u) {
-            drawLineChar(painter, x + (_fontWidth * i), y, _fontWidth, _fontHeight, code);
-        } else {
-            drawOtherChar(painter, x + (_fontWidth * i), y, _fontWidth, _fontHeight, code);
-        }
+        LineBlockCharacters::draw(painter, cellRect.translated(i * _fontWidth, 0), str[i],
+                                        useBoldPen);
     }
-
-    painter.restore();
 }
 
 void TerminalDisplay::setKeyboardCursorShape(Enum::CursorShapeEnum shape)
@@ -1135,6 +770,9 @@ void TerminalDisplay::drawCharacters(QPainter& painter,
         painter.setPen(color);
     }
 
+    const bool origClipping = painter.hasClipping();
+    const auto origClipRegion = painter.clipRegion();
+    painter.setClipRect(rect);
     // draw text
     if (isLineCharString(text) && !_useFontLineCharacters) {
         drawLineCharString(painter, rect.x(), rect.y(), text, style);
@@ -1145,14 +783,14 @@ void TerminalDisplay::drawCharacters(QPainter& painter,
         // This still allows RTL characters to be rendered in the RTL way.
         painter.setLayoutDirection(Qt::LeftToRight);
 
-        painter.setClipRect(rect);
         if (_bidiEnabled) {
             painter.drawText(rect.x(), rect.y() + _fontAscent + _lineSpacing, text);
         } else {
             painter.drawText(rect.x(), rect.y() + _fontAscent + _lineSpacing, LTR_OVERRIDE_CHAR + text);
         }
-        painter.setClipping(false);
     }
+    painter.setClipRegion(origClipRegion);
+    painter.setClipping(origClipping);
 }
 
 void TerminalDisplay::drawTextFragment(QPainter& painter ,
@@ -1450,7 +1088,7 @@ void TerminalDisplay::updateImage()
                     if (newLine[x + 0].character == 0u) {
                         continue;
                     }
-                    const bool lineDraw = newLine[x + 0].isLineChar();
+                    const bool lineDraw = LineBlockCharacters::canDraw(newLine[x + 0].character);
                     const bool doubleWidth = (x + 1 == columnsToUpdate) ? false : (newLine[x + 1].character == 0);
                     const RenditionFlags cr = newLine[x].rendition;
                     const CharacterColor clipboard = newLine[x].backgroundColor;
@@ -1471,7 +1109,7 @@ void TerminalDisplay::updateImage()
                                 ch.backgroundColor != clipboard ||
                                 (ch.rendition & ~RE_EXTENDED_CHAR) != (cr & ~RE_EXTENDED_CHAR) ||
                                 (dirtyMask[x + len] == 0) ||
-                                ch.isLineChar() != lineDraw ||
+                                LineBlockCharacters::canDraw(ch.character) != lineDraw ||
                                 nextIsDoubleWidth != doubleWidth) {
                             break;
                         }
@@ -1874,7 +1512,7 @@ void TerminalDisplay::drawContents(QPainter& paint, const QRect& rect)
                 }
             }
 
-            const bool lineDraw = _image[loc(x, y)].isLineChar();
+            const bool lineDraw = LineBlockCharacters::canDraw(_image[loc(x, y)].character);
             const bool doubleWidth = (_image[qMin(loc(x, y) + 1, _imageSize - 1)].character == 0);
             const CharacterColor currentForeground = _image[loc(x, y)].foregroundColor;
             const CharacterColor currentBackground = _image[loc(x, y)].backgroundColor;
diff --git a/tests/line_block_characters_table.py b/tests/line_block_characters_table.py
new file mode 100755
index 0000000..39422b4
--- /dev/null
+++ b/tests/line_block_characters_table.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python3
+
+# Prints tables with all characters supported by LineBlockCharactersDrawer,
+# one for normal weight and one for bold.
+
+first = 0x2500
+last = 0x259F
+
+cpPerLine = 32
+
+lineFmt = "\033[48;5;243;38;5;231m"
+
+def fmtLine(text):
+    return "{}\033[{}X {}\033[49;39m".format(lineFmt, cpPerLine*2+1, text)
+def fmtCh(text):
+    return "\033[48;5;231;38;5;16m{}{}".format(text, lineFmt)
+def fmtRefCh(text):
+    return "\033[48;5;252;38;5;16m{}{}".format(text, lineFmt)
+def setNoWrap(enable):
+    print("\033[?7l" if enable else "\033[?7h", end="")
+def setBold(enable):
+    print("\033[1m" if enable else "\033[21m", end="")
+def fmtBold(text):
+    return "\033[1m{}\033[21m".format(text)
+
+refChars = [["|", "│┃"], ["_-", "─━"], ["L", "└┗"], ["+", "┼╋"], ["=F", "╒╬"],
+            ["/", "╱"], ["\\", "╲"], ["X", "╳"]]
+boxes = \
+    "     +-----------+   *************   ,============,   ╲\\  ╱/\n" \
+    "     | ┌───────┐ |   @ ┏━━━━━━━┓ @   # ╔════════╗ #    ╲\\╱/ \n" \
+    "     | │ Light │ |   @ ┃ Heavy ┃ @   # ║ Double ║ #     ╳X  \n" \
+    "     | └───────┘ |   @ ┗━━━━━━━┛ @   # ╚════════╝ #    ╱/╲\\ \n" \
+    "     +-----------+   *************   \"============\"   ╱/  ╲\\\n" \
+
+lines = []
+for cp in range(first, last+1):
+    columnId = int((cp - first) % cpPerLine)
+    lineId = int((cp - first) / cpPerLine)
+    if columnId == 0:
+        lines.append([])
+    lines[lineId].append(chr(cp))
+
+setNoWrap(True)
+
+refCharsLine = " ".join(fmtRefCh(rc[0]) + fmtCh(rc[1]) for rc in refChars)
+print(fmtLine("{:8s} line width reference: {}".format("Normal", refCharsLine)))
+
+print(fmtLine(""))
+for line in lines:
+    print(fmtLine(" ".join(fmtCh(ch) for ch in line)))
+    print(fmtLine(""))
+
+print("\n" + boxes)
+
+setBold(True)
+refCharsLine = " ".join(fmtRefCh(rc[0]) + fmtCh(rc[1]) for rc in refChars)
+print(fmtLine("{:8s} line width reference: {}".format("Bold", refCharsLine)))
+
+print(fmtLine(""))
+for line in lines:
+    print(fmtLine(" ".join(fmtCh(ch) for ch in line)))
+    print(fmtLine(""))
+
+print("\n" + boxes)
+
+setBold(False)
+setNoWrap(False)
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 284160a..6b32433 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -2,19 +2,4 @@
 ### konsoleprofile command-line tool
 install(PROGRAMS konsoleprofile DESTINATION ${KDE_INSTALL_BINDIR})
 
-### Line graphics font
-###  Attempting to auto-create LineFont.h for multiple systems is a headache.
-###  If LineFont.h is needed to be recreated use:
-###     fontembedder LineFont.src > LineFont.h
-###  Then commit the new LineFont.h
-if(KONSOLE_BUILD_FONTEMBEDDER OR KONSOLE_GENERATE_LINEFONT)
-
-    find_package(Qt5Core ${QT_MIN_VERSION} CONFIG REQUIRED)
-
-    ### Font Embedder
-    set(fontembedder_SRCS fontembedder.cpp)
-    add_executable(fontembedder ${fontembedder_SRCS})
-    target_link_libraries(fontembedder Qt5::Core)
-endif()
-
 add_subdirectory( uni2characterwidth )
diff --git a/tools/fontembedder.cpp b/tools/fontembedder.cpp
deleted file mode 100644
index 7c6f2fb..0000000
--- a/tools/fontembedder.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
-    This file is part of Konsole, an X terminal.
-    Copyright 2005 by Maksim Orlovich <maksim@kde.org>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-    02110-1301  USA.
-*/
-
-// Standard
-#include <cstdlib>
-#include <iostream>
-#include <iomanip>
-
-// Qt
-#include <QFile>
-#include <QTextStream>
-#include <QDebug>
-
-using namespace std;
-
-static quint32 charVal(QChar val)
-{
-    if (val == QLatin1Char(' ')) {
-        return 0;
-    } else {
-        return 1;
-    }
-}
-
-static quint32 readGlyphLine(QTextStream& input)
-{
-    QString line = input.readLine();
-    while (line.length() < 5) {
-        line += QLatin1Char(' ');
-    }
-
-    quint32 val =  charVal(line[0]) |
-                   (charVal(line[1]) << 1)  |
-                   (charVal(line[2]) << 2)  |
-                   (charVal(line[3]) << 3)  |
-                   (charVal(line[4]) << 4);
-    return val;
-}
-
-static quint32 readGlyph(QTextStream& input)
-{
-    return readGlyphLine(input) |
-           (readGlyphLine(input) << 5) |
-           (readGlyphLine(input) << 10) |
-           (readGlyphLine(input) << 15) |
-           (readGlyphLine(input) << 20);
-}
-
-int main(int argc, char **argv)
-{
-    if (argc != 2) {
-        qWarning("usage: fontembedder LineFont.src > LineFont.h");
-        exit(1);
-    }
-    QFile inFile(QString::fromLocal8Bit(argv[1]));
-    if (!inFile.open(QIODevice::ReadOnly)) {
-        qWarning("Can not open %s", argv[1]);
-        exit(1);
-    }
-
-    QTextStream input(&inFile);
-
-    // Currently, for Konsole, the input glyphs file ranges from 0x2500
-    // (9472) to x257f (9599) so this 128 will handle it.  However, if
-    // more glyphs are added to the input file, this will be an issue.
-    quint32 glyphStates[128];
-    QMap<quint32, int> glyphMap;
-
-    for (unsigned int & glyphState : glyphStates) {
-        glyphState = 0; //nothing..
-    }
-
-    while (!input.atEnd()) {
-        QString line = input.readLine();
-        line = line.trimmed();
-        if (line.isEmpty()) {
-            continue; //Skip empty lines
-        }
-        if (line[0] == QLatin1Char('#')) {
-            continue; //Skip comments
-        }
-
-        //Must be a glyph ID.
-        int glyph = line.toInt(nullptr, 16);
-        if ((glyph < 0x2500) || (glyph > 0x257f)) {
-            qWarning("Invalid glyph number: %d aborting...", glyph);
-            exit(1);
-        } else {
-            glyph = glyph - 0x2500;
-
-            glyphStates[glyph] = readGlyph(input);
-            // qWarning()<<glyph<<";"<<glyphStates[glyph];
-
-            if (glyphMap.contains(glyphStates[glyph])) {
-            // FIXME: get this qWarning working again
-            //qWarning()<<"Code "<<glyph<<" and "<<glyphMap.value(glyphStates[glyph])<<"have the same glyph state"<<glyphStates[glyph];
-            }
-            glyphMap[glyphStates[glyph]] = glyph;
-        }
-    }
-
-    //Output.
-    cout << "// WARNING: Autogenerated by \"fontembedder " << argv[1] << "\".\n";
-    cout << "// You probably do not want to hand-edit this!\n\n";
-    cout << "static const quint32 LineChars[] = {\n";
-
-    //Nicely formatted: 8 per line, 16 lines
-    for (int line = 0; line < 128; line += 8) {
-        cout << "\t";
-        for (int col = line; col < line + 8; ++col) {
-            cout << "0x" << hex << setw(8) << setfill('0') << glyphStates[col];
-            if (col != 127) {
-                cout << ", ";
-            }
-        }
-        cout << "\n";
-    }
-    cout << "};\n";
-    return 0;
-}
-
-- 
cgit v1.1
openSUSE Build Service is sponsored by