KItinerary

uic9183ticketlayout.cpp
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 "logging.h"
9
10#include <QDateTime>
11#include <QDebug>
12
13#include <cstring>
14
15using namespace KItinerary;
16
17namespace KItinerary {
18
19class Uic9183TicketLayoutPrivate : public QSharedData
20{
21public:
22 Uic9183Block block;
23};
24
25}
26
27enum {
28 FieldHeaderSize = 13,
29};
30
31Uic9183TicketLayoutField::Uic9183TicketLayoutField() = default;
32
33// 2x field line, number as ascii text
34// 2x field column
35// 2x field height
36// 2x field width
37// 1x field format
38// 4x text length
39// Nx text content
40Uic9183TicketLayoutField::Uic9183TicketLayoutField(const Uic9183Block &block, int offset)
41 : m_offset(offset)
42{
43 const auto remainingSize = block.contentSize() - offset;
44 if (remainingSize <= FieldHeaderSize) { // too small
45 qCWarning(Log) << "Found too small U_TLAY field:" << remainingSize;
46 return;
47 }
48
49 // invalid format - we need to be very specific here, due to the workaround in ::next() below
50 if (!std::all_of(block.content() + offset, block.content() + offset + 8, isdigit)
51 || !std::all_of(block.content() + offset + 9, block.content() + offset + FieldHeaderSize, isdigit)) {
52 qCWarning(Log) << "Found U_TLAY field with invalid format";
53 return;
54 }
55
56 // size is too large
57 const auto fieldSize = Uic9183Utils::readAsciiEncodedNumber(block, offset + 9, 4) + FieldHeaderSize;
58 if (fieldSize + offset > block.contentSize()) {
59 qCWarning(Log) << "Found U_TLAY field with invalid size" << fieldSize << block.size();
60 return;
61 }
62
63 m_data = block;
64}
65
67{
68 return m_data.isNull();
69}
70
71Uic9183TicketLayoutField Uic9183TicketLayoutField::next() const
72{
73 const auto thisSize = size() + FieldHeaderSize;
74 const auto remaining = m_data.contentSize() - thisSize - m_offset;
75 if (remaining < 0) {
76 return {};
77 }
78
79 // search for the next field
80 // in theory this should always trigger at i == 0, unless
81 // the size field is wrong, which happens unfortunately
82 for (int i = 0; i < remaining - FieldHeaderSize; ++i) {
83 Uic9183TicketLayoutField f(m_data, m_offset + thisSize + i);
84 if (!f.isNull()) {
85 return f;
86 }
87 }
88
89 return {};
90}
91
92Uic9183TicketLayoutField Uic9183TicketLayout::firstField() const
93{
94 const auto contentSize = d->block.contentSize();
95 if (contentSize > 8) {
96 return Uic9183TicketLayoutField(d->block, 8);
97 }
98 return {};
99}
100
101
102// "U_TLAY" Block
103// 4x ticket layout type (e.g. "RCT2")
104// 4x field count
105// Nx fields (see Uic9183TicketLayoutField)
106Uic9183TicketLayout::Uic9183TicketLayout()
107 : d(new Uic9183TicketLayoutPrivate)
108{
109}
110
111Uic9183TicketLayout::Uic9183TicketLayout(const Uic9183Block &block)
112 : d(new Uic9183TicketLayoutPrivate)
113{
114 d->block = block;
115
116#if 0
117 std::vector<QString> out;
118 for (auto f = d->firstField(); !f.isNull(); f = f.next()) {
119 qDebug() << "Field:" << f.row() << f.column() << f.width() << f.height() << f.text() << f.size();
120 out.resize(std::max<int>(f.row() + 1, out.size()));
121 out[f.row()].resize(std::max(out[f.row()].size(), f.column() + f.width() + 1), QLatin1Char(' '));
122 out[f.row()].replace(f.column(), f.width(), f.text());
123 }
124 for (const auto &line : out) {
125 qDebug() << line;
126 }
127#endif
128}
129
130Uic9183TicketLayout::Uic9183TicketLayout(const Uic9183TicketLayout&) = default;
131Uic9183TicketLayout::~Uic9183TicketLayout() = default;
132Uic9183TicketLayout& Uic9183TicketLayout::operator=(const Uic9183TicketLayout&) = default;
133
135{
136 return Uic9183Utils::readUtf8String(d->block, 0, 4);
137}
138
140{
141 return !d->block.isNull() && d->block.contentSize() > 8 && d->block.version() == 1;
142}
143
144QString Uic9183TicketLayout::text(int row, int column, int width, int height) const
145{
146 QStringList s;
147 s.reserve(height);
148 for (int i = 0; i < height; ++i) {
149 s.push_back({});
150 }
151
152 for (auto f = firstField(); !f.isNull(); f = f.next()) {
153 // there's non-compliant samples out there with zero field sizes...
154 const auto effectiveHeight = std::max(f.height(), 1);
155 if (f.row() + effectiveHeight - 1 < row || f.row() > row + height - 1) {
156 continue;
157 }
158 const auto effectiveFieldWidth = f.width() > 0 ? f.width() : f.size();
159 if (f.column() + effectiveFieldWidth - 1 < column || f.column() > column + width - 1) {
160 continue;
161 }
162
163 // split field into lines
164 // TODO this needs to follow the U_TLAY word-wrapping algorithm?
165 const auto content = f.text();
166 const auto lines = QStringView(content).split(QLatin1Char('\n'));
167 // cut out the right part of the line
168 for (int i = 0; i < lines.size(); ++i) {
169 if (f.row() + i < row) {
170 continue;
171 }
172 if (f.row() + i > row + height - 1) {
173 break;
174 }
175
176 // TODO also truncate by w
177 const auto offset = column - f.column();
178 if (offset >= 0) {
179 s[f.row() + i - row] += lines.at(i).mid(offset).left(width);
180 } else {
181 s[f.row() + i - row] += QString(-offset, QLatin1Char(' '));
182 s[f.row() + i - row] += lines.at(i); // TODO truncate by width + offset
183 }
184 }
185 }
186 //qDebug() << "Result:" << row << column << width << height << s;
187 return s.join(QLatin1Char('\n'));
188}
189
190QSize Uic9183TicketLayout::size() const
191{
192 int width = 0;
193 int height = 0;
194 for (auto f = firstField(); !f.isNull(); f = f.next()) {
195 width = std::max(width, f.column() + f.width());
196 height = std::max(height, f.row() + std::max(1, f.height()));
197 }
198 return QSize(width, height);
199}
200
201std::vector<Uic9183TicketLayoutField> Uic9183TicketLayout::fields(int row, int column, int width, int height) const
202{
203 std::vector<Uic9183TicketLayoutField> result;
204 for (auto f = firstField(); !f.isNull(); f = f.next()) {
205 // there's non-compliant samples out there with zero field sizes...
206 const auto effectiveHeight = std::max(f.height(), 1);
207 if (f.row() + effectiveHeight - 1 < row || f.row() > row + height - 1) {
208 continue;
209 }
210 const auto effectiveFieldWidth = f.width() > 0 ? f.width() : f.size();
211 if (f.column() + effectiveFieldWidth - 1 < column || f.column() > column + width - 1) {
212 continue;
213 }
214 result.push_back(f);
215 }
216 return result;
217}
218
219std::vector<Uic9183TicketLayoutField> Uic9183TicketLayout::containedFields(int row, int column, int width, int height) const
220{
221 std::vector<Uic9183TicketLayoutField> result;
222 for (auto f = firstField(); !f.isNull(); f = f.next()) {
223 // there's non-compliant samples out there with zero field sizes...
224 const auto effectiveHeight = std::max(f.height(), 1);
225 if (f.row() + effectiveHeight - 1 > row + height - 1 || f.row() < row ) {
226 continue;
227 }
228 const auto effectiveFieldWidth = f.width() > 0 ? f.width() : f.size();
229 if (f.column() + effectiveFieldWidth - 1 > column + width + 1 || f.column() < column) {
230 continue;
231 }
232 result.push_back(f);
233 }
234 return result;
235}
236
237#include "moc_uic9183ticketlayout.cpp"
A data block from a UIC 918.3 ticket.
int contentSize() const
Returns the size of the content data.
bool isNull() const
Checks if the block is valid or empty/default constructed.
int size() const
Returns the size of the entire block data.
const char * content() const
Returns the payload data (not including the block header).
Low-level field entries in a U_TLAY block.
bool isNull
Size of the text content.
Parser for a U_TLAY block in a UIC 918-3 ticket container, such as a ERA TLB ticket.
bool isValid() const
Returns whether this is a valid U_TLAY layout block.
std::vector< Uic9183TicketLayoutField > fields(int row, int column, int width, int height) const
All fields covering the given area.
std::vector< Uic9183TicketLayoutField > containedFields(int row, int column, int width, int height) const
All fields contained in the given area.
Q_INVOKABLE QString text(int row, int column, int width, int height) const
Returns the text in the given coordinates.
Classes for reservation/travel data models, data extraction and data augmentation.
Definition berelement.h:17
const_reference at(qsizetype i) const const
void push_back(parameter_type value)
void reserve(qsizetype size)
QString join(QChar separator) const const
QList< QStringView > split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:49 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.