pdf: text that names no colour comes out black, not the palette
A QTextDocument takes its default text colour from the application palette, and markdown names no colour at all: in a window running a dark palette -- which is what a phone in dark mode hands Qt -- every plain paragraph was laid out #eeeeee on a white page. Printed, that is a blank document; on screen it reads as "the font is oddly faint". The first PDFs off the phone were exactly that. - PdfTools: paperDocument() sets the default stylesheet (body colour #000000) before setHtml, and writePdf() lays that out. A colour the document does name still wins, so an .html handed to the tool keeps its own -- checked with a #cc0000 document under both palettes. - --uitest: draws that document on a white image under a dark palette and looks for ink (238 before the fix, 0 after). Verified on the NX789J: md_to_pdf on the device now writes CID TrueType NotoSansCJKHK-Regular/-Bold text whose page renders at min=0.
This commit is contained in:
@@ -35,6 +35,7 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "core/AgentCore.h"
|
#include "core/AgentCore.h"
|
||||||
#include "core/ToolProvider.h"
|
#include "core/ToolProvider.h"
|
||||||
@@ -168,10 +169,9 @@ bool sourceHtml(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
|
|||||||
* strip. */
|
* strip. */
|
||||||
bool writePdf(const QString &html, const QString &path, const QString &title,
|
bool writePdf(const QString &html, const QString &path, const QString &title,
|
||||||
QString *err) {
|
QString *err) {
|
||||||
QTextDocument doc;
|
std::unique_ptr<QTextDocument> doc(paperDocument(html));
|
||||||
doc.setHtml(html);
|
|
||||||
if (!title.isEmpty())
|
if (!title.isEmpty())
|
||||||
doc.setMetaInformation(QTextDocument::DocumentTitle, title);
|
doc->setMetaInformation(QTextDocument::DocumentTitle, title);
|
||||||
|
|
||||||
QPdfWriter writer(path);
|
QPdfWriter writer(path);
|
||||||
writer.setPageSize(QPageSize(QPageSize::A4));
|
writer.setPageSize(QPageSize(QPageSize::A4));
|
||||||
@@ -186,17 +186,17 @@ bool writePdf(const QString &html, const QString &path, const QString &title,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const QRectF page = writer.pageLayout().paintRectPixels(writer.resolution());
|
const QRectF page = writer.pageLayout().paintRectPixels(writer.resolution());
|
||||||
doc.setPageSize(page.size());
|
doc->setPageSize(page.size());
|
||||||
const int pages = qMax(1, doc.pageCount());
|
const int pages = qMax(1, doc->pageCount());
|
||||||
for (int i = 0; i < pages; i++) {
|
for (int i = 0; i < pages; i++) {
|
||||||
if (i)
|
if (i)
|
||||||
writer.newPage();
|
writer.newPage();
|
||||||
painter.save();
|
painter.save();
|
||||||
painter.translate(page.topLeft());
|
painter.translate(page.topLeft());
|
||||||
painter.translate(0, -i * page.height());
|
painter.translate(0, -i * page.height());
|
||||||
doc.drawContents(&painter,
|
doc->drawContents(&painter,
|
||||||
QRectF(0, 0, page.width(), page.height())
|
QRectF(0, 0, page.width(), page.height())
|
||||||
.translated(0, i * page.height()));
|
.translated(0, i * page.height()));
|
||||||
painter.restore();
|
painter.restore();
|
||||||
}
|
}
|
||||||
painter.end();
|
painter.end();
|
||||||
@@ -285,6 +285,24 @@ int mdToPdfRun(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
/* The document writePdf lays out, with a colour filled in for text that names
|
||||||
|
* none of its own.
|
||||||
|
*
|
||||||
|
* Paper is white and has no theme, but a QTextDocument takes its default text
|
||||||
|
* colour from the application palette: in a window running a dark palette --
|
||||||
|
* which is what a phone in dark mode hands Qt -- every plain markdown
|
||||||
|
* paragraph came out #eeeeee on a white page, i.e. a PDF that looks blank
|
||||||
|
* when printed, and reads as "the font is oddly faint" on screen. Naming the
|
||||||
|
* colour makes the page depend on the paper rather than on the window. A
|
||||||
|
* colour the document does name still wins (an .html file somebody handed us
|
||||||
|
* keeps its own), so this only fills the holes. */
|
||||||
|
QTextDocument *paperDocument(const QString &html) {
|
||||||
|
auto *doc = new QTextDocument;
|
||||||
|
doc->setDefaultStyleSheet(QStringLiteral("body { color: #000000; }"));
|
||||||
|
doc->setHtml(html);
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
int registerQtTools(AgentCore *core) {
|
int registerQtTools(AgentCore *core) {
|
||||||
if (!core)
|
if (!core)
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
/* The engine wrapper of the front end (core/AgentCore.h). Only a pointer
|
/* The engine wrapper of the front end (core/AgentCore.h). Only a pointer
|
||||||
* travels through this header, so the class stays incomplete here. */
|
* travels through this header, so the class stays incomplete here. */
|
||||||
class AgentCore;
|
class AgentCore;
|
||||||
|
/* Same for the document md_to_pdf lays out (PdfTools.cpp): the caller owns
|
||||||
|
* it, and the test in ui/UiTest.cpp inspects how it draws. */
|
||||||
|
class QTextDocument;
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
@@ -48,6 +51,10 @@ int registerPlatformTools(AgentCore *core);
|
|||||||
* (they are Qt, not a platform API): today md_to_pdf, owned by "app:qt".
|
* (they are Qt, not a platform API): today md_to_pdf, owned by "app:qt".
|
||||||
* Called next to registerPlatformTools(). */
|
* Called next to registerPlatformTools(). */
|
||||||
int registerQtTools(AgentCore *core);
|
int registerQtTools(AgentCore *core);
|
||||||
|
/* The document md_to_pdf lays out from `html`: the page's own defaults
|
||||||
|
* applied (text with no colour of its own is black, not the window's palette
|
||||||
|
* colour). The caller owns the result. */
|
||||||
|
QTextDocument *paperDocument(const QString &html);
|
||||||
/* Drop them again (they belong to the owners "app:*"). */
|
/* Drop them again (they belong to the owners "app:*"). */
|
||||||
void unregisterPlatformTools(AgentCore *core);
|
void unregisterPlatformTools(AgentCore *core);
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
|
#include <QPalette>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
@@ -22,14 +23,17 @@
|
|||||||
#include <QQmlContext>
|
#include <QQmlContext>
|
||||||
#include <QQuickWindow>
|
#include <QQuickWindow>
|
||||||
#include <QTest>
|
#include <QTest>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
#include <QVariantMap>
|
#include <QVariantMap>
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "cst_agent.h"
|
#include "cst_agent.h"
|
||||||
#include "cst_attach.h"
|
#include "cst_attach.h"
|
||||||
|
#include "core/ToolProvider.h"
|
||||||
#include "ui/SessionImages.h"
|
#include "ui/SessionImages.h"
|
||||||
#include "ui/ThemeIcons.h"
|
#include "ui/ThemeIcons.h"
|
||||||
|
|
||||||
@@ -482,6 +486,36 @@ int runUiTest(AgentCore &core, const QString &size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Paper is white whatever the window's palette says: markdown carries no
|
||||||
|
* colour, so a dark window turned every plain paragraph #eeeeee -- a PDF
|
||||||
|
* that looked blank. Draw it on a white image and look for ink. */
|
||||||
|
{
|
||||||
|
const QPalette was = QGuiApplication::palette();
|
||||||
|
QPalette darkPalette = was;
|
||||||
|
darkPalette.setColor(QPalette::Text, QColor(0xee, 0xee, 0xee));
|
||||||
|
darkPalette.setColor(QPalette::WindowText, QColor(0xee, 0xee, 0xee));
|
||||||
|
QGuiApplication::setPalette(darkPalette);
|
||||||
|
std::unique_ptr<QTextDocument> page(app::paperDocument(
|
||||||
|
QStringLiteral("<html><body>text that names no colour of its own"
|
||||||
|
"</body></html>")));
|
||||||
|
QImage paper(320, 60, QImage::Format_RGB32);
|
||||||
|
paper.fill(0xffffffffu);
|
||||||
|
QPainter ink(&paper);
|
||||||
|
page->drawContents(&ink);
|
||||||
|
ink.end();
|
||||||
|
int darkest = 255;
|
||||||
|
for (int y = 0; y < paper.height(); y++) {
|
||||||
|
const QRgb *row = reinterpret_cast<const QRgb *>(paper.constScanLine(y));
|
||||||
|
for (int x = 0; x < paper.width(); x++)
|
||||||
|
darkest = qMin(darkest, qGray(row[x]));
|
||||||
|
}
|
||||||
|
check(darkest < 64,
|
||||||
|
QStringLiteral("a dark window still lays out black ink on paper "
|
||||||
|
"(darkest pixel %1)")
|
||||||
|
.arg(darkest));
|
||||||
|
QGuiApplication::setPalette(was);
|
||||||
|
}
|
||||||
|
|
||||||
/* new sessions: the engine moves, the view empties, the store grows */
|
/* new sessions: the engine moves, the view empties, the store grows */
|
||||||
const int before = core.sessions().size();
|
const int before = core.sessions().size();
|
||||||
clickMouse(win, QStringLiteral("newSessionButton"));
|
clickMouse(win, QStringLiteral("newSessionButton"));
|
||||||
|
|||||||
Reference in New Issue
Block a user