uic9183ticketlayout.cpp 5.54 KB
Newer Older
1
/*
2
    SPDX-FileCopyrightText: 2019 Volker Krause <vkrause@kde.org>
3

4
    SPDX-License-Identifier: LGPL-2.0-or-later
5
6
7
*/

#include "uic9183ticketlayout.h"
8
#include "uic9183block.h"
9
10
11
12
#include "logging.h"

#include <QDateTime>
#include <QDebug>
13
#include <QSize>
14
15
16
17
18
19
20
21
22
23

#include <cstring>

using namespace KItinerary;

namespace KItinerary {

class Uic9183TicketLayoutPrivate : public QSharedData
{
public:
24
    Uic9183Block block;
25
26
27
28
};

}

29
30
31
32
enum {
    FieldHeaderSize = 13,
};

33
Uic9183TicketLayoutField::Uic9183TicketLayoutField() = default;
34

35
36
37
38
39
40
41
// 2x field line, number as ascii text
// 2x field column
// 2x field height
// 2x field width
// 1x field format
// 4x text length
// Nx text content
42
43
Uic9183TicketLayoutField::Uic9183TicketLayoutField(const Uic9183Block &block, int offset)
    : m_offset(offset)
44
{
45
46
47
    const auto remainingSize = block.contentSize() - offset;
    if (remainingSize <= FieldHeaderSize) { // too small
        qCWarning(Log) << "Found too small U_TLAY field:" << remainingSize;
48
49
50
        return;
    }

51
52
53
    // invalid format - we need to be very specific here, due to the workaround in ::next() below
    if (!std::all_of(block.content() + offset, block.content() + offset + 8, isdigit)
     || !std::all_of(block.content() + offset + 9, block.content() + offset + FieldHeaderSize, isdigit)) {
54
55
56
57
58
        qCWarning(Log) << "Found U_TLAY field with invalid format";
        return;
    }

    // size is too large
59
60
61
    const auto fieldSize = Uic9183Utils::readAsciiEncodedNumber(block, offset + 9, 4) + FieldHeaderSize;
    if (fieldSize + offset > block.contentSize()) {
        qCWarning(Log) << "Found U_TLAY field with invalid size" << fieldSize << block.size();
62
63
64
        return;
    }

65
    m_data = block;
66
67
}

68
bool Uic9183TicketLayoutField::isNull() const
69
{
70
    return m_data.isNull();
71
72
73
74
}

Uic9183TicketLayoutField Uic9183TicketLayoutField::next() const
{
75
76
    const auto thisSize = size() + FieldHeaderSize;
    const auto remaining = m_data.contentSize() - thisSize - m_offset;
77
78
79
80
81
82
83
    if (remaining < 0) {
        return {};
    }

    // search for the next field
    // in theory this should always trigger at i == 0, unless
    // the size field is wrong, which happens unfortunately
84
85
    for (int i = 0; i < remaining - FieldHeaderSize; ++i) {
        Uic9183TicketLayoutField f(m_data, m_offset + thisSize + i);
86
87
88
89
90
91
92
93
        if (!f.isNull()) {
            return f;
        }
    }

    return {};
}

94
Uic9183TicketLayoutField Uic9183TicketLayout::firstField() const
95
{
96
    const auto contentSize = d->block.contentSize();
97
    if (contentSize > 8) {
98
        return Uic9183TicketLayoutField(d->block, 8);
99
100
101
102
103
    }
    return {};
}


104
// "U_TLAY" Block
105
106
107
108
109
110
111
112
// 4x ticket layout type (e.g. "RCT2")
// 4x field count
// Nx fields (see Uic9183TicketLayoutField)
Uic9183TicketLayout::Uic9183TicketLayout()
    : d(new Uic9183TicketLayoutPrivate)
{
}

113
Uic9183TicketLayout::Uic9183TicketLayout(const Uic9183Block &block)
114
115
    : d(new Uic9183TicketLayoutPrivate)
{
116
    d->block = block;
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

#if 0
    std::vector<QString> out;
    for (auto f = d->firstField(); !f.isNull(); f = f.next()) {
        qDebug() << "Field:" << f.row() << f.column() << f.width() << f.height() << f.text() << f.size();
        out.resize(std::max<int>(f.row() + 1, out.size()));
        out[f.row()].resize(std::max(out[f.row()].size(), f.column() + f.width() + 1), QLatin1Char(' '));
        out[f.row()].replace(f.column(), f.width(), f.text());
    }
    for (const auto &line : out) {
        qDebug() << line;
    }
#endif
}

Uic9183TicketLayout::Uic9183TicketLayout(const Uic9183TicketLayout&) = default;
Uic9183TicketLayout::~Uic9183TicketLayout() = default;
Uic9183TicketLayout& Uic9183TicketLayout::operator=(const Uic9183TicketLayout&) = default;

QString Uic9183TicketLayout::type() const
{
Volker Krause's avatar
Volker Krause committed
138
    return Uic9183Utils::readUtf8String(d->block, 0, 4);
139
140
141
142
}

bool Uic9183TicketLayout::isValid() const
{
143
    return !d->block.isNull() && d->block.contentSize() > 8 && d->block.version() == 1;
144
145
146
147
148
149
150
151
152
153
}

QString Uic9183TicketLayout::text(int row, int column, int width, int height) const
{
    QStringList s;
    s.reserve(height);
    for (int i = 0; i < height; ++i) {
        s.push_back({});
    }

154
    for (auto f = firstField(); !f.isNull(); f = f.next()) {
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
        if (f.row() + f.height() - 1 < row || f.row() > row + height - 1) {
            continue;
        }
        if (f.column() + f.width() - 1 < column || f.column() > column + width - 1) {
            continue;
        }

        // split field into lines
        // TODO this needs to follow the U_TLAY word-wrapping algorithm?
        const auto content = f.text();
        const auto lines = content.splitRef(QLatin1Char('\n'));

        // cut out the right part of the line
        for (int i = 0; i < lines.size(); ++i) {
            if (f.row() + i < row) {
                continue;
            }
            if (f.row() + i > row + height - 1) {
                break;
            }

            // TODO also truncate by w
            const auto offset = column - f.column();
            if (offset >= 0) {
                s[f.row() + i - row] += lines.at(i).mid(offset).left(width);
            } else {
                s[f.row() + i - row] += lines.at(i); // TODO left padding by offset, truncate by width + offset
            }
        }
    }
    //qDebug() << "Result:" << row << column << width << height << s;
    return s.join(QLatin1Char('\n'));
}
188
189
190
191

QSize Uic9183TicketLayout::size() const
{
    int width = 0, height = 0;
192
    for (auto f = firstField(); !f.isNull(); f  = f.next()) {
193
194
195
196
197
        width = std::max(width, f.column() + f.width());
        height = std::max(height, f.row() + f.height());
    }
    return QSize(width, height);
}