PdfDraw now supports color emoji, Fix in Gtk DrawText, refactored font height adjustment in replacement fonts

This commit is contained in:
Mirek Fidler 2022-01-12 23:33:37 +01:00
parent adcd29c609
commit f084e9a612
11 changed files with 235 additions and 99 deletions

View file

@ -38,15 +38,15 @@ void FontSysData::Init(Font font, int angle)
cairo_matrix_init_scale(font_matrix, fh, fh);
if(font.IsItalic() && !(FTFace(font)->style_flags & FT_STYLE_FLAG_ITALIC)) {
cairo_matrix_t sheer[1];
cairo_matrix_init_identity(sheer);
sheer->xy = -0.2;
cairo_matrix_multiply(font_matrix, font_matrix, sheer);
}
if(angle)
cairo_matrix_rotate(font_matrix, -angle * M_2PI / 3600);
cairo_matrix_rotate(font_matrix, -angle * M_PI / 1800);
if(font.IsItalic() && !(FTFace(font)->style_flags & FT_STYLE_FLAG_ITALIC)) { // Synthetic italic
cairo_matrix_t shear[1];
cairo_matrix_init_identity(shear);
shear->xy = -0.165;
cairo_matrix_multiply(font_matrix, shear, font_matrix);
}
cairo_font_options_t *opt = cairo_font_options_create();
scaled_font = cairo_scaled_font_create(font_face, font_matrix, ctm, opt);

View file

@ -426,15 +426,22 @@ bool Replace(Font fnt, int chr, Font& rfnt)
f.Face(fi);
if(IsNormal_nc(f, chr)) {
int a = fnt.GetAscent();
int d = fnt.GetDescent();
static WString apple_kbd = "⌘⌃⇧⌥"; // do not make these smaller it looks ugly...
if(f.GetAscent() > a && apple_kbd.Find(chr) < 0) {
LLOG("Original font: " << fnt << " " << fnt.GetAscent() << " " << f.GetDescent() <<
", replacement " << f << " " << f.GetAscent() << " " << f.GetDescent());
if((f.GetAscent() > a || f.GetDescent() > d) && apple_kbd.Find(chr) < 0) {
static sFontMetricsReplacement cache[256];
int q = CombineHash(fnt, f) & 255;
if(cache[q].src != fnt || cache[q].dst != f) {
cache[q].src = fnt;
cache[q].dst = f;
while(f.GetAscent() > a && f.GetHeight() > 1) {
f.Height(max(1, f.GetHeight() - max(1, f.GetHeight() / 20)));
double h = f.GetHeight();
f.Height(min(h * a / max(1, f.GetAscent()), h * d / max(1, f.GetDescent())) + 1);
while((f.GetAscent() > a || f.GetDescent() > d) && f.GetHeight() > 1) {
f.Height(max(1, f.GetHeight() - 1/*max(1, f.GetHeight() / 20)*/));
LLOG("Original font: " << fnt << " " << fnt.GetAscent() << " " << f.GetDescent() <<
", downsized " << f << " " << f.GetAscent() << " " << f.GetDescent());
}
cache[q].mdst = f;
}

View file

@ -155,9 +155,9 @@ CommonFontInfo GetFontInfoSys(Font font)
else
*fi.path = 0;
if(font.GetFaceInfo() & Font::COLORIMG) {
if(font.GetFaceInfo() & Font::COLORIMG) { // Experimental estimate for cairo results
fi.colorimg_cy = fi.ascent + fi.descent;
int h = font.GetHeight();
int h = 4 * font.GetHeight() / 3;
fi.ascent = h * fi.ascent / fi.colorimg_cy;
fi.descent = h - fi.ascent;
}

View file

@ -41,6 +41,7 @@ struct Xform2D {
static Xform2D Scale(double scale);
static Xform2D Rotation(double fi);
static Xform2D Sheer(double fi);
static Xform2D SheerX(double fi);
static Xform2D Map(Pointf s1, Pointf s2, Pointf s3); // maps 0,0 -> s3, 1,0 -> s1, 0,1 -> s2
static Xform2D Map(Pointf s1, Pointf s2, Pointf s3, Pointf t1, Pointf t2, Pointf t3);

View file

@ -90,11 +90,17 @@ Xform2D Xform2D::Rotation(double fi)
Xform2D Xform2D::Sheer(double fi)
{
Xform2D m;
m.x.x = m.y.y = 1;
m.x.y = atan(fi);
return m;
}
Xform2D Xform2D::SheerX(double fi)
{
Xform2D m;
m.y.x = atan(fi);
return m;
}
Xform2D Xform2D::Identity()
{
Xform2D m;

View file

@ -296,7 +296,7 @@ PdfDraw::OutlineInfo PdfDraw::GetOutlineInfo(Font fnt)
of.sitalic = of.standard_ttf = false;
TTFReader ttf;
if(ttf.Open(fnt, false, true)) {
if((fnt.GetFaceInfo() & Font::COLORIMG) == 0 && ttf.Open(fnt, false, true)) {
of.standard_ttf = true;
of.sitalic = fnt.IsItalic() && (ttf.head.macStyle & 2) == 0;
of.sbold = fnt.IsBold() && (ttf.head.macStyle & 1) == 0;
@ -310,70 +310,134 @@ PdfDraw::OutlineInfo PdfDraw::GetOutlineInfo(Font fnt)
enum { FONTHEIGHT_TTF = -9999 };
Image RenderGlyph(int cx, int x, Font font, int chr, int py, int pcy, Color fg, Color bg);
PdfDraw::CGlyph PdfDraw::ColorGlyph(Font fnt, int chr)
{
auto key = MakeTuple(fnt, chr);
int q = color_glyph.Find(key);
if(q >= 0)
return color_glyph[q];
CGlyph cg;
cg.sz = { fnt[chr], fnt.GetCy() };
cg.x = 0;
int l = fnt.GetLeftSpace(chr);
if(l < 0) {
cg.x = -l;
cg.sz.cx -= l;
}
int r = fnt.GetRightSpace(chr);
if(r < 0)
cg.sz.cx -= r;
Image m[2];
for(int i = 0; i < 2; i++)
m[i] = RenderGlyph(cg.sz.cx, cg.x, fnt, chr, 0, cg.sz.cy, Blue(), i ? Black() : White());
Image cm = RecreateAlpha(m[0], m[1]);
cg.image = PdfImage(cm, cm.GetSize());
color_glyph.Add(key, cg);
return cg;
}
void PdfDraw::DrawTextOp(int x, int y, int angle, const wchar *s, Font fnt,
Color ink, int n, const int *dx)
{
LLOG("DrawTextOp " << x << ", " << y << "angle: " << angle << ", text: " << s << ", font " << fnt);
LLOG("DrawTextOp " << x << ", " << y << " angle: " << angle << ", text: " << s << ", font " << fnt);
if(!n) return;
int h = fnt.GetHeight();
if(h == 0)
fnt.Height(100);
if(h < 0)
fnt.Height(-h);
Font ff = fnt;
int fh = fnt.GetHeight();
OutlineInfo of = GetOutlineInfo(fnt);
if(of.standard_ttf)
fnt.Height(FONTHEIGHT_TTF);
String txt;
PutrgColor(ink);
PutRGColor(ink);
double sina = 0, cosa = 1;
if(angle)
Draw::SinCos(angle, sina, cosa);
int nbld = 0;
int sbld = 1;
if(of.sbold) {
nbld = abs(ff.GetHeight()) / 30;
sbld = min(5, nbld);
}
int posx = 0;
for(int q = 0; q <= nbld; q += sbld) {
page << "BT ";
posx = q;
M22 m;
if(of.sitalic)
m.c = 0.165;
if(angle)
m.Mul(cosa, sina, -sina, cosa);
bool straight = true;
OutlineInfo of = GetOutlineInfo(fnt);
Xform2D m;
if(of.sitalic) {
m = m.SheerX(0.165);
straight = false;
}
if(angle) {
straight = false;
m = Xform2D::Rotation(-angle * M_PI / 1800.0) * m;
}
auto Fmt = [](double x) { return FormatF(x, 5); };
if(fnt.GetFaceInfo() & Font::COLORIMG) {
int fi = -1;
bool straight = (fabs(m.a - 1) <= 1e-8 && fabs(m.b) <= 1e-8 && fabs(m.c) <= 1e-8 && fabs(m.d - 1) <= 1e-8);
Pointf prev(0, 0);
for(int i = 0; i < n; i++) {
Pointf next(Pt(x + posx * cosa + fround(ff.GetAscent() * sina)),
Pt(pgsz.cy - (y - posx * sina) - fround(ff.GetAscent() * cosa)));
CharPos fp = GetCharPos(fnt, s[i]);
if(fi != fp.fi) {
fi = fp.fi;
PutFontHeight(fi, fh);
}
CGlyph cg = ColorGlyph(fnt, s[i]);
page << "q ";
if(straight)
page << (next.x - prev.x) << ' ' << (next.y - prev.y) << " Td";
else
page << m.a << ' ' << m.b << ' ' << m.c << ' ' << m.d << ' ' << next.x << ' ' << next.y << " Tm";
page << " <" << FormatIntHex(fp.ci, 2);
page << "> Tj\n";
posx += dx ? dx[i] : ff[s[i]];
prev = next;
}
page << "ET\n";
page << Ptf(cg.sz.cx) << " 0 0 " << Ptf(cg.sz.cy) << ' '
<< Ptf(x + posx + cg.x) << ' ' << Ptf(pgsz.cy - y - cg.sz.cy);
else {
Xform2D mm = m * Xform2D::Scale(Pt(cg.sz.cx), Pt(cg.sz.cy));
page << Fmt(mm.x.x) << ' ' << Fmt(mm.x.y) << ' ' << Fmt(mm.y.x) << ' ' << Fmt(mm.y.y)
<< ' ' << Ptf(x + posx * cosa + cg.sz.cx * sina)
<< ' ' << Ptf(pgsz.cy - (y - posx * sina) - cg.sz.cy * cosa);
}
page << " cm /Image" << cg.image + 1 << " Do Q\n";
if(q == 0 && url.GetCount()) { // For now, only 'zero angle' text can have links
posx += dx ? dx[i] : fnt[s[i]];
}
if(url.GetCount()) { // For now, only 'zero angle' text can have links
UrlInfo& u = page_url.At(offset.GetCount()).Add();
u.rect = RectC(x, y, posx, ff.GetCy()).Offseted(current_offset);
u.rect = RectC(x, y, posx, fnt.GetCy()).Offseted(current_offset);
u.url = url;
}
}
else {
int h = fnt.GetHeight();
if(h == 0)
fnt.Height(100);
if(h < 0)
fnt.Height(-h);
Font ff = fnt;
int fh = fnt.GetHeight();
if(of.standard_ttf)
fnt.Height(FONTHEIGHT_TTF);
String txt;
PutrgColor(ink);
PutRGColor(ink);
int nbld = 0;
int sbld = 1;
if(of.sbold) {
nbld = abs(ff.GetHeight()) / 30;
sbld = clamp(nbld, 1, 5);
}
for(int q = 0; q <= nbld; q += sbld) {
page << "BT ";
posx = q;
int fi = -1;
Pointf prev(0, 0);
for(int i = 0; i < n; i++) {
Pointf next(Pt(x + posx * cosa + ff.GetAscent() * sina),
Pt(pgsz.cy - (y - posx * sina) - ff.GetAscent() * cosa));
CharPos fp = GetCharPos(fnt, s[i]);
if(fi != fp.fi) {
fi = fp.fi;
PutFontHeight(fi, fh);
}
if(straight)
page << Fmt(next.x - prev.x) << ' ' << Fmt(next.y - prev.y) << " Td";
else
page << Fmt(m.x.x) << ' ' << Fmt(m.x.y) << ' ' << Fmt(m.y.x) << ' ' << Fmt(m.y.y)
<< ' ' << Fmt(next.x) << ' ' << Fmt(next.y) << " Tm";
page << " <" << FormatIntHex(fp.ci, 2);
page << "> Tj\n";
posx += dx ? dx[i] : ff[s[i]];
prev = next;
if(q == 0 && url.GetCount()) { // For now, only 'zero angle' text can have links
UrlInfo& u = page_url.At(offset.GetCount()).Add();
u.rect = RectC(x, y, posx, ff.GetCy()).Offseted(current_offset);
u.url = url;
}
}
page << "ET\n";
}
}
}
void PdfDraw::Escape(const String& data)
@ -384,8 +448,6 @@ void PdfDraw::Escape(const String& data)
this->data = data.Mid(5);
}
Image RenderGlyph(int cx, int x, Font font, int chr, int py, int pcy, Color fg, Color bg);
PdfDraw::RGlyph PdfDraw::RasterGlyph(Font fnt, int chr)
{
RGlyph rg;
@ -842,8 +904,7 @@ String PdfDraw::Finish(const PdfSignatureInfo *sign)
int fa = fnt.GetCy() - fnt.GetInternal();
for(int i = 0; i < cs.GetCount(); i++)
out << ' ' << 1000 * fnt[cs[i]] / fa;
out <<
"]\n";
out << "]\n";
out << "/Resources " << resources << " 0 R\n"
<< "/FirstChar 0 /LastChar " << cs.GetCount() - 1 <<" /ToUnicode "
@ -853,7 +914,7 @@ String PdfDraw::Finish(const PdfSignatureInfo *sign)
}
else {
TTFReader ttf;
if(!ttf.Open(pdffont.GetKey(i)))
if(!ttf.Open(fnt))
return Null;
String name = FormatIntAlpha(i + 1, true);

View file

@ -3,7 +3,7 @@
#include <Draw/Draw.h>
#include <plugin/z/z.h>
#include <Painter/Painter.h>
namespace Upp {
@ -314,7 +314,8 @@ private:
Vector<Point> offset_stack;
Point current_offset;
inline double Pt(double dot) { return 0.12 * dot; }
double Pt(double dot) { return 0.12 * dot; }
String Ptf(double dot) { return FormatF(Pt(dot), 5); }
int Pos() { return offset.GetCount() + 1; }
int BeginObj();
@ -337,22 +338,6 @@ private:
OutlineInfo GetOutlineInfo(Font fnt);
struct M22 {
double a, b, c, d;
void Mul(double a1, double b1, double c1, double d1) {
M22 t;
t.a = a * a1 + b * c1;
t.b = a * b1 + b * d1;
t.c = c * a1 + d * c1;
t.d = c * b1 + d * d1;
*this = t;
}
M22(double a, double b, double c, double d) : a(a), b(b), c(c), d(d) {}
M22() : a(1), b(0), c(0), d(1) {}
};
void Init(int pagecx, int pagecy, int margin, bool pdfa);
struct RGlyph : Moveable<RGlyph> {
@ -361,8 +346,17 @@ private:
int x;
int color_image = -1;
};
struct CGlyph : Moveable<CGlyph> {
Size sz;
int x;
int image;
};
VectorMap<Tuple<Font, int>, CGlyph> color_glyph;
int PdfImage(const Image& img, const Rect& src);
CGlyph ColorGlyph(Font fnt, int chr);
RGlyph RasterGlyph(Font fnt, int chr);
public:

View file

@ -1,7 +1,8 @@
description "PDF output as Draw derived class\377128,0,255";
uses
Draw;
Draw,
Painter;
file
PdfDraw.h options(BUILDER_OPTION) PCH,

View file

@ -3,28 +3,48 @@
using namespace Upp;
CONSOLE_APP_MAIN
void DoPaint(Draw& w, int hg, int x1, int x2, int n = INT_MAX)
{
PdfDraw pdf;
int cy = 0;
pdf.DrawImage(5000, 20, 100, 100, CtrlImg::exclamation());
// for(int face = Font::SERIF; face < Font::GetFaceCount(); face++)
int face = 19;
w.DrawImage(x2, 20, 100, 100, CtrlImg::exclamation());
for(int face = Font::SERIF; face < min(n, Font::GetFaceCount()); face++)
{
Font fnt(face, 100);
Font fnt(face, hg);
Cout() << fnt << "\n";
LOG(face << ' ' << fnt << ", TTF: " << fnt.IsTrueType());
String txt = AsString(fnt) + " 訓民正音 (훈민정음) 😜 🤪 ";
pdf.DrawText(0, cy, txt, fnt, Black);
pdf.DrawText(3000, cy, AsString(fnt) << ' ' << face, StdFont(100), Black);
w.DrawRect(0, cy, GetTextSize(txt, fnt).cx, fnt.GetAscent(), Blend(White(), LtBlue()));
w.DrawRect(0, cy + fnt.GetAscent(), GetTextSize(txt, fnt).cx, fnt.GetDescent(), Blend(White(), LtRed()));
w.DrawText(0, cy, txt, fnt, Black);
w.DrawText(x1, cy, AsString(fnt) << ' ' << face, StdFont(hg), Black);
cy += fnt.GetLineHeight();
if(cy > 6000) {
pdf.EndPage();
pdf.StartPage();
auto *pdf = dynamic_cast<PdfDraw *>(&w);
if(pdf) {
pdf->EndPage();
pdf->StartPage();
}
cy = 0;
}
}
String p = GetHomeDirFile("pdf.pdf");
SaveFile(p, pdf.Finish());
LaunchWebBrowser(p);
}
struct MyApp : TopWindow {
void Paint(Draw& w) override {
Size sz = GetSize();
w.DrawRect(sz, White());
DoPaint(w, 24, sz.cx / 2, sz.cx - 100, 10);
}
};
GUI_APP_MAIN
{
PdfDraw pdf;
DoPaint(pdf, 100, 3000, 5000);
String p = GetHomeDirFile("pdf.pdf");
String s = pdf.Finish();
DDUMP(s.GetCount());
SaveFile(p, s);
LaunchWebBrowser(p);
MyApp().Run();
}

View file

@ -0,0 +1,35 @@
#include <CtrlLib/CtrlLib.h>
#include <PdfDraw/PdfDraw.h>
using namespace Upp;
void DoPaint(Draw& w, int hg, int x1, int y1, int x2, int y2)
{
int cy = 0;
w.DrawImage(x2, 20, 100, 100, CtrlImg::exclamation());
Font fnt = Serif(hg);
for(int i = 0; i < 3600; i += 450) {
String txt = "Text with emoji: 訓음 😜🤪 " + AsString(i);
w.DrawText(x1, y1, i, txt, fnt, Black);
w.DrawText(x2, y2, i, txt, fnt().Italic(), Black);
}
}
struct MyApp : TopWindow {
void Paint(Draw& w) override {
Size sz = GetSize();
w.DrawRect(sz, White());
DoPaint(w, 24, sz.cx / 4, sz.cy / 2, sz.cx - sz.cx / 4, sz.cy / 2);
}
};
GUI_APP_MAIN
{
PdfDraw pdf;
DoPaint(pdf, 100, 2000, 2000, 2200, 5000);
String p = GetHomeDirFile("pdf.pdf");
String s = pdf.Finish();
SaveFile(p, s);
LaunchWebBrowser(p);
MyApp().Run();
}

View file

@ -0,0 +1,11 @@
uses
Core,
PdfDraw,
CtrlLib;
file
PdfText.cpp;
mainconfig
"" = "GUI";