diff --git a/uppsrc/CtrlCore/GtkDrawText.cpp b/uppsrc/CtrlCore/GtkDrawText.cpp index 568b95b1d..41d749fba 100644 --- a/uppsrc/CtrlCore/GtkDrawText.cpp +++ b/uppsrc/CtrlCore/GtkDrawText.cpp @@ -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); diff --git a/uppsrc/Draw/FontCR.cpp b/uppsrc/Draw/FontCR.cpp index d3e009214..2e9de474e 100644 --- a/uppsrc/Draw/FontCR.cpp +++ b/uppsrc/Draw/FontCR.cpp @@ -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; } diff --git a/uppsrc/Draw/FontFc.cpp b/uppsrc/Draw/FontFc.cpp index 788960fd0..b8f01ea4a 100644 --- a/uppsrc/Draw/FontFc.cpp +++ b/uppsrc/Draw/FontFc.cpp @@ -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; } diff --git a/uppsrc/Painter/Painter.h b/uppsrc/Painter/Painter.h index a279d861c..306265eaa 100644 --- a/uppsrc/Painter/Painter.h +++ b/uppsrc/Painter/Painter.h @@ -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); diff --git a/uppsrc/Painter/Xform2D.cpp b/uppsrc/Painter/Xform2D.cpp index 252a7d6c7..d8fc7db52 100644 --- a/uppsrc/Painter/Xform2D.cpp +++ b/uppsrc/Painter/Xform2D.cpp @@ -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; diff --git a/uppsrc/PdfDraw/PdfDraw.cpp b/uppsrc/PdfDraw/PdfDraw.cpp index cfbd69d1f..bdaf7bf3e 100644 --- a/uppsrc/PdfDraw/PdfDraw.cpp +++ b/uppsrc/PdfDraw/PdfDraw.cpp @@ -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); diff --git a/uppsrc/PdfDraw/PdfDraw.h b/uppsrc/PdfDraw/PdfDraw.h index a92c27e7e..a8a9c79e4 100644 --- a/uppsrc/PdfDraw/PdfDraw.h +++ b/uppsrc/PdfDraw/PdfDraw.h @@ -3,7 +3,7 @@ #include #include - +#include namespace Upp { @@ -314,7 +314,8 @@ private: Vector 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 { @@ -361,8 +346,17 @@ private: int x; int color_image = -1; }; + + struct CGlyph : Moveable { + Size sz; + int x; + int image; + }; + + VectorMap, CGlyph> color_glyph; int PdfImage(const Image& img, const Rect& src); + CGlyph ColorGlyph(Font fnt, int chr); RGlyph RasterGlyph(Font fnt, int chr); public: diff --git a/uppsrc/PdfDraw/PdfDraw.upp b/uppsrc/PdfDraw/PdfDraw.upp index 4e07d9743..b22ed0328 100644 --- a/uppsrc/PdfDraw/PdfDraw.upp +++ b/uppsrc/PdfDraw/PdfDraw.upp @@ -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, diff --git a/upptst/PdfText2/PdfText.cpp b/upptst/PdfText2/PdfText.cpp index 36a250f6e..fb1b9c92f 100644 --- a/upptst/PdfText2/PdfText.cpp +++ b/upptst/PdfText2/PdfText.cpp @@ -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(&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(); } diff --git a/upptst/PdfTextAngle/PdfText.cpp b/upptst/PdfTextAngle/PdfText.cpp new file mode 100644 index 000000000..3fbe8a0f4 --- /dev/null +++ b/upptst/PdfTextAngle/PdfText.cpp @@ -0,0 +1,35 @@ +#include +#include + +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(); +} diff --git a/upptst/PdfTextAngle/PdfTextAngle.upp b/upptst/PdfTextAngle/PdfTextAngle.upp new file mode 100644 index 000000000..56ef50382 --- /dev/null +++ b/upptst/PdfTextAngle/PdfTextAngle.upp @@ -0,0 +1,11 @@ +uses + Core, + PdfDraw, + CtrlLib; + +file + PdfText.cpp; + +mainconfig + "" = "GUI"; +