Improve built-in line/block characters drawing

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
This commit is contained in:
Mariusz Glebocki
2019-02-07 01:36:52 +01:00
parent 1c8ba770b1
commit 13132fc77b
10 changed files with 862 additions and 1363 deletions

View File

@@ -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

View File

@@ -53,21 +53,6 @@ const RenditionFlags RE_STRIKEOUT = (1 << 8);
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
@@ -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) {

722
src/LineBlockCharacters.cpp Normal file
View File

@@ -0,0 +1,722 @@
/*
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");
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);
Q_FALLTHROUGH();
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);
Q_FALLTHROUGH();
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);
Q_FALLTHROUGH();
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);
Q_FALLTHROUGH();
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);
Q_FALLTHROUGH();
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);
Q_FALLTHROUGH();
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);
Q_FALLTHROUGH();
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

57
src/LineBlockCharacters.h Normal file
View File

@@ -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

View File

@@ -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
};

View File

@@ -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
|||
|||
|||
|
|

View File

@@ -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"
@@ -73,6 +73,7 @@
#include "IncrementalSearchBar.h"
#include "Profile.h"
#include "ViewManager.h" // for colorSchemeForProfile. // TODO: Rewrite this.
#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,382 +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!
Q_FALLTHROUGH();
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!
Q_FALLTHROUGH();
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;

View File

@@ -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)

View File

@@ -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 )

View File

@@ -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;
}