Merge pull request #1362 from ispyisail/feature/folio-paper-size

Add paper sizes A0–A5 to the folio dimensions
This commit is contained in:
ispyisail
2026-10-07 20:54:31 +13:00
committed by GitHub
7 changed files with 308 additions and 2 deletions
+229
View File
@@ -18,8 +18,89 @@
#include "borderpropertieswidget.h"
#include "../diagram.h"
#include "../qetapp.h"
#include "../titleblocktemplate.h"
#include "ui_borderpropertieswidget.h"
#include <iterator>
#include <tuple>
namespace {
/// ISO 216 A sizes in mm, landscape
struct PaperSize { const char *name; int width_mm; int height_mm; };
const PaperSize paper_sizes[] = {
{"A0", 1189, 841},
{"A1", 841, 594},
{"A2", 594, 420},
{"A3", 420, 297},
{"A4", 297, 210},
{"A5", 210, 148}
};
/// The PDF export draws one scene unit as one pixel at 96 dpi
int mmToPx(int mm) { return qRound(mm * 96.0 / 25.4); }
/// The PDF export rounds a page within 3 pt of a paper size to that
/// size, and 4 px left over already rounds to 3.0 pt and misses
const int exact_fit = 3;
/// Smallest column width or row height a paper size may give
const int min_cell = 10;
/// Fewest columns or rows a folio may have: BorderTitleBlock raises
/// any lower count to 3 (MIN_COLUMN_COUNT, MIN_ROW_COUNT)
const int min_count = 3;
/**
@brief fitCells
Split @p available pixels into cells close to @p current pixels.
First choice: cells within 15 %, then 30 %, of the current size
that leave at most exact_fit pixels, the closest to the current
size. Failing that, cells within 15 % that leave the fewest pixels.
@return the number of cells and their size
*/
QPair<int, int> fitCells(int available, int current, int min_size, int max_count)
{
current = qMax(current, min_size);
auto size_for = [&](int count, double spread) {
const int size = available / count;
return (size < min_size || qAbs(size - current) > current * spread)
? 0 : size;
};
for (double spread : {0.15, 0.30})
{
QPair<int, int> best(0, 0);
for (int count = min_count ; count <= max_count ; ++count)
{
const int size = size_for(count, spread);
if (size && available - count * size <= exact_fit &&
(!best.first || qAbs(size - current) < qAbs(best.second - current)))
best = qMakePair(count, size);
}
if (best.first) return best;
}
QPair<int, int> best(0, 0);
std::tuple<int, int> best_score(available, available);
for (int count = min_count ; count <= max_count ; ++count)
{
const int size = size_for(count, 0.15);
const std::tuple<int, int> score(available - count * size,
qAbs(size - current));
if (size && score < best_score) {
best_score = score;
best = qMakePair(count, size);
}
}
if (best.first) return best;
//No size near the current one when the cell count is capped
const int count = qBound(min_count, qRound(double(available) / current), max_count);
return qMakePair(count, available / count);
}
}
/**
@brief BorderPropertiesWidget::BorderPropertiesWidget
default constructor
@@ -31,6 +112,30 @@ BorderPropertiesWidget::BorderPropertiesWidget(const BorderProperties &bp, QWidg
ui(new Ui::BorderPropertiesWidget)
{
ui->setupUi(this);
ui->m_paper_cb->addItem(tr("Personnalisé"));
for (const PaperSize &paper : paper_sizes)
ui->m_paper_cb->addItem(QString::fromLatin1(paper.name));
ui->m_orientation_cb->addItem(tr("Paysage"));
ui->m_orientation_cb->addItem(tr("Portrait"));
if (TitleBlockTemplate *tbt = QETApp::defaultTitleBlockTemplate())
m_titleblock_height = tbt->height();
//activated() is only emitted for a choice the user makes
auto paper_picked = [this]() {
m_paper_picked = ui->m_paper_cb->currentIndex() > 0;
applyPaperSize();
};
connect(ui->m_paper_cb, qOverload<int>(&QComboBox::activated),
this, paper_picked);
connect(ui->m_orientation_cb, qOverload<int>(&QComboBox::activated),
this, paper_picked);
for (QSpinBox *sb : {ui->m_colums_count_sp, ui->m_columns_width_sp,
ui->m_rows_count_sp, ui->m_rows_height_sp})
connect(sb, qOverload<int>(&QSpinBox::valueChanged),
this, &BorderPropertiesWidget::showMatchingPaperSize);
setProperties(bp);
}
@@ -57,6 +162,7 @@ void BorderPropertiesWidget::setProperties(const BorderProperties &bp)
ui -> m_rows_count_sp ->setValue (m_properties.rows_count);
ui -> m_rows_height_sp ->setValue (m_properties.rows_height);
ui -> m_display_rows_cb ->setChecked (m_properties.display_rows);
showMatchingPaperSize();
}
/**
@@ -83,3 +189,126 @@ void BorderPropertiesWidget::setReadOnly(const bool &ro)
{
ui->border_gb->setDisabled(ro);
}
/**
@brief BorderPropertiesWidget::setTitleBlockSize
Tell the widget the title block the folio uses: its height counts in
the size of the page. If the user picked a paper size in this widget,
the columns and rows are fitted again so the folio keeps that size;
otherwise they are left alone.
@param height : height of the title block template
@param edge : edge of the folio the title block sits on
*/
void BorderPropertiesWidget::setTitleBlockSize(int height, Qt::Edge edge)
{
m_titleblock_height = height;
m_titleblock_edge = edge;
if (m_paper_picked)
applyPaperSize();
else
showMatchingPaperSize();
}
/**
@brief BorderPropertiesWidget::applyPaperSize
Set the columns and rows so the folio, title block included, has the
size of the chosen paper. The column width and row height stay close
to the current ones.
*/
void BorderPropertiesWidget::applyPaperSize()
{
const int index = ui->m_paper_cb->currentIndex() - 1;
ui->m_orientation_cb->setEnabled(index >= 0);
if (index < 0) return;
int columns, column_width, rows, row_height;
if (!fitPaperSize(index, ui->m_orientation_cb->currentIndex() == 0,
columns, column_width, rows, row_height))
{
//The title block leaves no room on this paper
showMatchingPaperSize();
return;
}
m_updating = true;
ui->m_colums_count_sp->setValue(columns);
ui->m_columns_width_sp->setValue(column_width);
ui->m_rows_count_sp->setValue(rows);
ui->m_rows_height_sp->setValue(row_height);
m_updating = false;
}
/**
@brief BorderPropertiesWidget::fitPaperSize
Compute the columns and rows that give the folio, title block
included, the size of a paper, with cells close to the current ones.
@param index : index of the paper in paper_sizes
@param landscape : true for landscape, false for portrait
@return false if the paper is too small for the title block
*/
bool BorderPropertiesWidget::fitPaperSize(int index, bool landscape,
int &columns, int &column_width,
int &rows, int &row_height) const
{
const PaperSize &paper = paper_sizes[index];
const int page_width = mmToPx(landscape ? paper.width_mm : paper.height_mm);
const int page_height = mmToPx(landscape ? paper.height_mm : paper.width_mm);
//The page is one pixel larger than the border, for its line
int width = page_width - 1 - qRound(m_properties.rows_header_width);
int height = page_height - 1 - qRound(m_properties.columns_header_height);
if (m_titleblock_edge == Qt::BottomEdge)
height -= m_titleblock_height;
else
width -= m_titleblock_height;
if (width < min_cell * min_count || height < min_cell * min_count)
return false;
const QPair<int, int> c = fitCells(width,
ui->m_columns_width_sp->value(),
min_cell,
ui->m_colums_count_sp->maximum());
const QPair<int, int> r = fitCells(height,
ui->m_rows_height_sp->value(),
min_cell,
ui->m_rows_count_sp->maximum());
columns = c.first;
column_width = c.second;
rows = r.first;
row_height = r.second;
return true;
}
/**
@brief BorderPropertiesWidget::showMatchingPaperSize
Show the paper size the folio has, or "Custom" if it has none: the
folio has a paper size when choosing it would change nothing.
*/
void BorderPropertiesWidget::showMatchingPaperSize()
{
if (m_updating) return;
m_paper_picked = false;
int found = 0;
int orientation = ui->m_orientation_cb->currentIndex();
for (int i = 0 ; i < int(std::size(paper_sizes)) && !found ; ++i)
{
for (int o = 0 ; o < 2 ; ++o)
{
int columns, column_width, rows, row_height;
if (fitPaperSize(i, o == 0, columns, column_width, rows, row_height) &&
columns == ui->m_colums_count_sp->value() &&
column_width == ui->m_columns_width_sp->value() &&
rows == ui->m_rows_count_sp->value() &&
row_height == ui->m_rows_height_sp->value())
{
found = i + 1;
orientation = o;
break;
}
}
}
ui->m_paper_cb->setCurrentIndex(found);
ui->m_orientation_cb->setCurrentIndex(orientation);
ui->m_orientation_cb->setEnabled(found > 0);
}
+16 -2
View File
@@ -41,12 +41,26 @@ class BorderPropertiesWidget : public QWidget
void setProperties(const BorderProperties &bp);
const BorderProperties &properties();
void setReadOnly (const bool &ro);
private slots:
void setTitleBlockSize(int height, Qt::Edge edge);
private:
void applyPaperSize();
void showMatchingPaperSize();
bool fitPaperSize(int index, bool landscape,
int &columns, int &column_width,
int &rows, int &row_height) const;
Ui::BorderPropertiesWidget *ui;
BorderProperties m_properties;
/// height of the title block the folio is drawn with, and
/// the edge it sits on: it counts in the size of the page
int m_titleblock_height = 0;
Qt::Edge m_titleblock_edge = Qt::BottomEdge;
/// true while the widget itself changes the spin boxes
bool m_updating = false;
/// true once the user picked a paper size, until the columns or
/// rows are edited by hand
bool m_paper_picked = false;
};
#endif // BORDERPROPERTIESWIDGET_H
+19
View File
@@ -80,7 +80,24 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="m_paper_label">
<property name="text">
<string>Format :</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="m_paper_cb">
<property name="toolTip">
<string>Règle les colonnes et les lignes pour que le folio, cartouche compris, ait la taille de ce format de papier, à 96 points par pouce comme l'export PDF.</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QComboBox" name="m_orientation_cb"/>
</item>
<item row="3" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -105,6 +122,8 @@
<tabstop>m_rows_height_sp</tabstop>
<tabstop>m_display_columns_cb</tabstop>
<tabstop>m_display_rows_cb</tabstop>
<tabstop>m_paper_cb</tabstop>
<tabstop>m_orientation_cb</tabstop>
</tabstops>
<resources/>
<connections/>
+4
View File
@@ -139,6 +139,10 @@ NewDiagramPage::NewDiagramPage(QETProject *project,
}
connect(ipw, &TitleBlockPropertiesWidget::openAutoNumFolioEditor, this, &NewDiagramPage::changeToAutoFolioTab);
bpw -> setTitleBlockSize(ipw -> currentTitleBlockHeight(),
ipw -> currentTitleBlockEdge());
connect(ipw, &TitleBlockPropertiesWidget::titleBlockSizeChanged,
bpw, &BorderPropertiesWidget::setTitleBlockSize);
// main tab widget
QTabWidget *tab_widget = new QTabWidget(this);
+4
View File
@@ -65,6 +65,10 @@ DiagramPropertiesDialog::DiagramPropertiesDialog(Diagram *diagram, QWidget *pare
titleblock_infos -> setReadOnly(diagram_is_read_only);
connect(titleblock_infos, &TitleBlockPropertiesWidget::openAutoNumFolioEditor, this, &DiagramPropertiesDialog::editAutoFolioNum);
border_infos -> setTitleBlockSize(titleblock_infos -> currentTitleBlockHeight(),
titleblock_infos -> currentTitleBlockEdge());
connect(titleblock_infos, &TitleBlockPropertiesWidget::titleBlockSizeChanged,
border_infos, &BorderPropertiesWidget::setTitleBlockSize);
//titleblock_infos->setMinimumSize(590,480); //Minimum Size needed for correct display
//Conductor widget
+33
View File
@@ -336,6 +336,30 @@ QString TitleBlockPropertiesWidget::currentTitleBlockTemplateName() const
return QString();
}
/**
@brief TitleBlockPropertiesWidget::currentTitleBlockHeight
@return the height of the current title block template
*/
int TitleBlockPropertiesWidget::currentTitleBlockHeight() const
{
const TitleBlockTemplate *tbt = nullptr;
if (!currentTitleBlockTemplateName().isEmpty())
tbt = templateForIndex(ui -> m_tbt_cb -> currentIndex());
if (!tbt)
tbt = QETApp::defaultTitleBlockTemplate();
return tbt ? tbt -> height() : 0;
}
/**
@brief TitleBlockPropertiesWidget::currentTitleBlockEdge
@return the edge of the folio the title block is displayed at
*/
Qt::Edge TitleBlockPropertiesWidget::currentTitleBlockEdge() const
{
return ui -> m_display_at_cb -> currentIndex() == 0 ? Qt::BottomEdge
: Qt::RightEdge;
}
/**
@brief TitleBlockPropertiesWidget::addCollection
add a collection of title block available in the combo box
@@ -388,6 +412,15 @@ void TitleBlockPropertiesWidget::initDialog(
this,
&TitleBlockPropertiesWidget::changeCurrentTitleBlockTemplate);
auto size_changed = [this]() {
emit titleBlockSizeChanged(currentTitleBlockHeight(),
currentTitleBlockEdge());
};
connect(ui->m_tbt_cb, qOverload<int>(&QComboBox::currentIndexChanged),
this, size_changed);
connect(ui->m_display_at_cb, qOverload<int>(&QComboBox::currentIndexChanged),
this, size_changed);
if (project!= nullptr){
keys_2 = project -> folioAutoNum().keys();
foreach (QString str, keys_2) { ui -> auto_page_cb -> addItem(str); }
+3
View File
@@ -70,6 +70,8 @@ class TitleBlockPropertiesWidget : public QWidget
void setTitleBlockTemplatesVisible(const bool &visible);
void setReadOnly (const bool &ro);
int currentTitleBlockHeight() const;
Qt::Edge currentTitleBlockEdge() const;
static void addTemplateVariables(
DiagramContext &context,
@@ -96,6 +98,7 @@ class TitleBlockPropertiesWidget : public QWidget
signals:
void set_auto_page_num() const;
void openAutoNumFolioEditor (QString);
void titleBlockSizeChanged(int height, Qt::Edge edge);
private:
Ui::TitleBlockPropertiesWidget *ui;