diff --git a/newdraw/Painter/Approximate.cpp b/uppsrc/Painter/Approximate.cpp similarity index 100% rename from newdraw/Painter/Approximate.cpp rename to uppsrc/Painter/Approximate.cpp diff --git a/newdraw/Painter/BufferPainter.h b/uppsrc/Painter/BufferPainter.h similarity index 100% rename from newdraw/Painter/BufferPainter.h rename to uppsrc/Painter/BufferPainter.h diff --git a/newdraw/Painter/Context.cpp b/uppsrc/Painter/Context.cpp similarity index 100% rename from newdraw/Painter/Context.cpp rename to uppsrc/Painter/Context.cpp diff --git a/newdraw/Painter/Copying b/uppsrc/Painter/Copying similarity index 100% rename from newdraw/Painter/Copying rename to uppsrc/Painter/Copying diff --git a/newdraw/Painter/Dasher.cpp b/uppsrc/Painter/Dasher.cpp similarity index 100% rename from newdraw/Painter/Dasher.cpp rename to uppsrc/Painter/Dasher.cpp diff --git a/newdraw/Painter/DrawOp.cpp b/uppsrc/Painter/DrawOp.cpp similarity index 100% rename from newdraw/Painter/DrawOp.cpp rename to uppsrc/Painter/DrawOp.cpp diff --git a/newdraw/Painter/Fillers.cpp b/uppsrc/Painter/Fillers.cpp similarity index 100% rename from newdraw/Painter/Fillers.cpp rename to uppsrc/Painter/Fillers.cpp diff --git a/newdraw/Painter/Fillers.h b/uppsrc/Painter/Fillers.h similarity index 100% rename from newdraw/Painter/Fillers.h rename to uppsrc/Painter/Fillers.h diff --git a/newdraw/Painter/FontWin32.cpp b/uppsrc/Painter/FontWin32.cpp similarity index 100% rename from newdraw/Painter/FontWin32.cpp rename to uppsrc/Painter/FontWin32.cpp diff --git a/newdraw/Painter/FontX11.cpp b/uppsrc/Painter/FontX11.cpp similarity index 100% rename from newdraw/Painter/FontX11.cpp rename to uppsrc/Painter/FontX11.cpp diff --git a/newdraw/Painter/Gradient.cpp b/uppsrc/Painter/Gradient.cpp similarity index 100% rename from newdraw/Painter/Gradient.cpp rename to uppsrc/Painter/Gradient.cpp diff --git a/newdraw/Painter/Image.cpp b/uppsrc/Painter/Image.cpp similarity index 100% rename from newdraw/Painter/Image.cpp rename to uppsrc/Painter/Image.cpp diff --git a/newdraw/Painter/Interpolator.cpp b/uppsrc/Painter/Interpolator.cpp similarity index 100% rename from newdraw/Painter/Interpolator.cpp rename to uppsrc/Painter/Interpolator.cpp diff --git a/newdraw/Painter/Mask.cpp b/uppsrc/Painter/Mask.cpp similarity index 100% rename from newdraw/Painter/Mask.cpp rename to uppsrc/Painter/Mask.cpp diff --git a/newdraw/Painter/OnPath.cpp b/uppsrc/Painter/OnPath.cpp similarity index 100% rename from newdraw/Painter/OnPath.cpp rename to uppsrc/Painter/OnPath.cpp diff --git a/newdraw/Painter/PaintPainting.icpp b/uppsrc/Painter/PaintPainting.icpp similarity index 100% rename from newdraw/Painter/PaintPainting.icpp rename to uppsrc/Painter/PaintPainting.icpp diff --git a/newdraw/Painter/Painter.cpp b/uppsrc/Painter/Painter.cpp similarity index 100% rename from newdraw/Painter/Painter.cpp rename to uppsrc/Painter/Painter.cpp diff --git a/newdraw/Painter/Painter.h b/uppsrc/Painter/Painter.h similarity index 100% rename from newdraw/Painter/Painter.h rename to uppsrc/Painter/Painter.h diff --git a/newdraw/Painter/Painter.hpp b/uppsrc/Painter/Painter.hpp similarity index 100% rename from newdraw/Painter/Painter.hpp rename to uppsrc/Painter/Painter.hpp diff --git a/newdraw/Painter/Painter.upp b/uppsrc/Painter/Painter.upp similarity index 100% rename from newdraw/Painter/Painter.upp rename to uppsrc/Painter/Painter.upp diff --git a/newdraw/Painter/PainterPath.cpp b/uppsrc/Painter/PainterPath.cpp similarity index 100% rename from newdraw/Painter/PainterPath.cpp rename to uppsrc/Painter/PainterPath.cpp diff --git a/newdraw/Painter/Painting.cpp b/uppsrc/Painter/Painting.cpp similarity index 100% rename from newdraw/Painter/Painting.cpp rename to uppsrc/Painter/Painting.cpp diff --git a/newdraw/Painter/Painting.h b/uppsrc/Painter/Painting.h similarity index 100% rename from newdraw/Painter/Painting.h rename to uppsrc/Painter/Painting.h diff --git a/newdraw/Painter/Path.cpp b/uppsrc/Painter/Path.cpp similarity index 100% rename from newdraw/Painter/Path.cpp rename to uppsrc/Painter/Path.cpp diff --git a/newdraw/Painter/RadialGradient.cpp b/uppsrc/Painter/RadialGradient.cpp similarity index 100% rename from newdraw/Painter/RadialGradient.cpp rename to uppsrc/Painter/RadialGradient.cpp diff --git a/newdraw/Painter/Rasterizer.cpp b/uppsrc/Painter/Rasterizer.cpp similarity index 100% rename from newdraw/Painter/Rasterizer.cpp rename to uppsrc/Painter/Rasterizer.cpp diff --git a/newdraw/Painter/Rasterizer3.h b/uppsrc/Painter/Rasterizer3.h similarity index 100% rename from newdraw/Painter/Rasterizer3.h rename to uppsrc/Painter/Rasterizer3.h diff --git a/newdraw/Painter/RasterizerClip.cpp b/uppsrc/Painter/RasterizerClip.cpp similarity index 100% rename from newdraw/Painter/RasterizerClip.cpp rename to uppsrc/Painter/RasterizerClip.cpp diff --git a/newdraw/Painter/Render.cpp b/uppsrc/Painter/Render.cpp similarity index 100% rename from newdraw/Painter/Render.cpp rename to uppsrc/Painter/Render.cpp diff --git a/newdraw/Painter/RenderChar.cpp b/uppsrc/Painter/RenderChar.cpp similarity index 100% rename from newdraw/Painter/RenderChar.cpp rename to uppsrc/Painter/RenderChar.cpp diff --git a/newdraw/Painter/Stroker.cpp b/uppsrc/Painter/Stroker.cpp similarity index 100% rename from newdraw/Painter/Stroker.cpp rename to uppsrc/Painter/Stroker.cpp diff --git a/newdraw/Painter/SvgArc.cpp b/uppsrc/Painter/SvgArc.cpp similarity index 100% rename from newdraw/Painter/SvgArc.cpp rename to uppsrc/Painter/SvgArc.cpp diff --git a/newdraw/Painter/Transformer.cpp b/uppsrc/Painter/Transformer.cpp similarity index 100% rename from newdraw/Painter/Transformer.cpp rename to uppsrc/Painter/Transformer.cpp diff --git a/newdraw/Painter/Xform2D.cpp b/uppsrc/Painter/Xform2D.cpp similarity index 100% rename from newdraw/Painter/Xform2D.cpp rename to uppsrc/Painter/Xform2D.cpp diff --git a/newdraw/Painter/init b/uppsrc/Painter/init similarity index 100% rename from newdraw/Painter/init rename to uppsrc/Painter/init diff --git a/newdraw/Painter/srcimp.tpp/SubpixelFiller$en-us.tpp b/uppsrc/Painter/srcimp.tpp/SubpixelFiller$en-us.tpp similarity index 100% rename from newdraw/Painter/srcimp.tpp/SubpixelFiller$en-us.tpp rename to uppsrc/Painter/srcimp.tpp/SubpixelFiller$en-us.tpp diff --git a/uppsrc/PdfDraw/Copying b/uppsrc/PdfDraw/Copying new file mode 100644 index 000000000..f135f3633 --- /dev/null +++ b/uppsrc/PdfDraw/Copying @@ -0,0 +1,21 @@ +Copyright 1998-2008 The U++ Project. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE U++ PROJECT ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/uppsrc/PdfDraw/PdfDraw.cpp b/uppsrc/PdfDraw/PdfDraw.cpp new file mode 100644 index 000000000..b50f4b12e --- /dev/null +++ b/uppsrc/PdfDraw/PdfDraw.cpp @@ -0,0 +1,834 @@ +#include "PdfDraw.h" + +NAMESPACE_UPP + +#define LDUMP(x) // DUMP(x) +#define LLOG(x) // LOG(x) + +#define PDF_COMPRESS +#define USE_TTF + +dword PdfDraw::GetInfo() const +{ + return DOTS; +} + +Size PdfDraw::GetPageSize() const +{ + return pgsz; +} + +void PdfDraw::Init(int pagecx, int pagecy, int _margin) +{ + Clear(); + margin = _margin; + pgsz.cx = pagecx; + pgsz.cy = pagecy; + pgsz += margin; +} + +void PdfDraw::Clear() +{ + out.Clear(); + page.Clear(); + offset.Clear(); + out << "%PDF-1.3\n"; + out << "%\xf1\xf2\xf3\xf4\n\n"; +} + +int PdfDraw::BeginObj() +{ + offset.Add(out.GetLength()); + out << offset.GetCount() << " 0 obj\n"; + return offset.GetCount(); +} + +void PdfDraw::EndObj() +{ + out << "endobj\n\n"; +} + +void PdfDraw::PutRect(const Rect& rc) +{ + page << Pt(rc.left) << ' ' << Pt(pgsz.cy - rc.bottom) << ' ' + << Pt(rc.Width()) << ' ' << Pt(rc.Height()) << " re\n"; +} + +int PdfDraw::PutStream(const String& data, const String& keys) +{ +#ifdef PDF_COMPRESS + String c = ZCompress(data); + if(c.GetLength() < data.GetLength()) { + BeginObj(); + out << "<< " << keys + << "/Length " << c.GetLength() << " " + << "/Length1 " << data.GetLength() << " " + << "/Filter /FlateDecode " + << " >>\n" + << "stream\r\n" << c << "\r\nendstream\n"; + EndObj(); + return offset.GetCount(); + } +#endif + BeginObj(); + out << "<< " << keys << " /Length " << data.GetLength() << + " /Length1 "<< data.GetLength() << " >>\n" + << "stream\r\n" << data << "\r\nendstream\n"; + EndObj(); + return offset.GetCount(); +} + +void PdfDraw::PutrgColor(Color rg) +{ + if(IsNull(rgcolor) || rg != rgcolor) + page << PdfColor(rgcolor = rg) << " rg\n"; +} + +void PdfDraw::PutRGColor(Color RG) +{ + if(IsNull(RGcolor) || RG != RGcolor) + page << PdfColor(RGcolor = RG) << " RG\n"; +} + +void PdfDraw::PutLineWidth(int lw) +{ + lw = max(Nvl(lw, 0), 5); + if(linewidth != lw) + page << Pt(linewidth = lw) << " w\n"; +} + +void PdfDraw::StartPage() +{ + rgcolor = RGcolor = Null; + fontid = -1; + textht = Null; + linewidth = -1; + if(margin) + OffsetOp(Point(margin, margin)); +} + +void PdfDraw::EndPage() +{ + if(margin) + EndOp(); + PutStream(page); + page.Clear(); +} + +void PdfDraw::BeginOp() +{ + page << "q\n"; +} + +void PdfDraw::EndOp() +{ + fontid = -1; + textht = Null; + rgcolor = RGcolor = Null; + linewidth = -1; + page << "Q\n"; +} + +void PdfDraw::OffsetOp(Point p) +{ + page << "q "; + if(p.x || p.y) + page << "1 0 0 1 " << Pt(p.x) << ' ' << Pt(-p.y) << " cm\n"; +} + +bool PdfDraw::ClipOp(const Rect& r) +{ + page << "q "; + PutRect(r); + page << "W* n\n"; + return true; +} + +bool PdfDraw::ClipoffOp(const Rect& r) +{ + page << "q "; + PutRect(r); + page << "W* n\n"; + if(r.left || r.top) + page << "1 0 0 1 " << Pt(r.left) << ' ' << Pt(-r.top) << " cm\n"; + return true; +} + +bool PdfDraw::ExcludeClipOp(const Rect& r) +{ + return true; // TODO +/* + if(r.left <= actual_clip.left && r.right >= actual_clip.right) { + if(r.top <= actual_clip.top) actual_clip.top = max(actual_clip.top, r.bottom); + if(r.bottom >= actual_clip.bottom) actual_clip.bottom = min(actual_clip.bottom, r.top); + } + if(r.top <= actual_clip.top && r.bottom >= actual_clip.bottom) { + if(r.left <= actual_clip.left) actual_clip.left = max(actual_clip.left, r.right); + if(r.right >= actual_clip.right) actual_clip.right = min(actual_clip.right, r.left); + } + + PutRect(actual_clip); + PutRect(r & actual_clip); + page << "W* n\n"; + return !actual_clip.IsEmpty(); +*/ +} + +bool PdfDraw::IntersectClipOp(const Rect& r) +{ + PutRect(r); + page << "W* n\n"; + return true; +} + +bool PdfDraw::IsPaintingOp(const Rect&) const +{ + return true; +} + +PdfDraw::CharPos PdfDraw::GetCharPos(Font fnt, wchar chr) +{ + fnt.Underline(false); + VectorMap& fc = fontchars.GetAdd(fnt); + int q = fc.Find(chr); + if(q >= 0) + return fc[q]; + CharPos& p = fc.Add(chr); + q = pdffont.FindLast(fnt); + if(q < 0 || pdffont[q].GetCount() > 250) { + p.fi = pdffont.GetCount(); + p.ci = 0; + pdffont.Add(fnt).Add(chr); + } + else { + p.fi = q; + p.ci = pdffont[q].GetCount(); + pdffont[q].Add(chr); + } + return p; +} + +void PdfDraw::FlushText(int dx, int fi, int height, const String& txt) +{ + if(fi < 0) + return; + if(dx) + page << Pt(dx) << " 0 Td "; + PutFontHeight(fi, height); + page << "<" << txt << "> Tj\n"; +} + +String PdfDraw::PdfColor(Color c) +{ + return NFormat("%3nf %3nf %3nf", c.GetR() / 255.0, c.GetG() / 255.0, c.GetB() / 255.0); +} + +void PdfDraw::PutFontHeight(int fi, double ht) +{ + if(fi != fontid || IsNull(textht) || ht != textht) + page << "/F" << ((fontid = fi) + 1) << ' ' << Pt(textht = ht) << " Tf\n"; +} + +PdfDraw::OutlineInfo PdfDraw::GetOutlineInfo(Font fnt) +{ + fnt.Height(0); + int q = outline_info.Find(fnt); + if(q >= 0) + return outline_info[q]; + OutlineInfo of; + of.sitalic = of.ttf = false; + +#ifdef USE_TTF +#ifdef PLATFORM_WIN32 + ScreenDraw info; + info.SetFont(fnt().Underline(false)); + TEXTMETRIC tm; + GetTextMetrics(info.GetHandle(), &tm); + if(tm.tmPitchAndFamily & TMPF_TRUETYPE) { + of.ttf = true; + int c = GetOutlineTextMetrics(info.GetHandle(), 0, NULL); + if(c > 0) { + Buffer h(c); + OUTLINETEXTMETRIC *otm = (OUTLINETEXTMETRIC *)~h; + GetOutlineTextMetrics(info.GetHandle(), c, otm); + of.sitalic = otm->otmItalicAngle == 0 && fnt.IsItalic(); + } + } +#endif +#ifdef PLATFORM_X11 + FontInfo fi = fnt.Info(); + String fn = fi.GetFileName(); + String ext = ToLower(GetFileExt(fn)); + if(ext == ".ttf" || ext == ".otf") { + String data = LoadFile(fn); + TTFReader ttf; + if(ttf.Open(data, false, true)) { + of.ttf = true; + of.sitalic = ttf.post.italicAngle == 0 && fnt.IsItalic(); + } + } +#endif +#endif + + outline_info.Add(fnt, of); + + return of; +} + +void PdfDraw::DrawTextOp(int x, int y, int angle, const wchar *s, Font fnt, + Color ink, int n, const int *dx) +{ + if(!n) return; + if(fnt.GetHeight() == 0) + fnt.Height(100); + FontInfo ff = fnt.Info(); + #ifdef PLATFORM_WIN32 + int fh = ff.GetHeight() - ff.GetInternal();//TODO + #endif + #ifdef PLATFORM_X11 + int fh = fnt.GetHeight(); + #endif + OutlineInfo of = GetOutlineInfo(fnt); + if(of.ttf) + fnt.Height(0); + String txt; + PutrgColor(ink); + PutRGColor(ink); + page << "BT "; + double sina = 0, cosa = 1; + int posx = 0; + if(angle || dx || (of.sitalic && !fnt.IsItalic())) { + M22 m; + if(of.sitalic) + m.c = 0.165; + if(angle) { + Draw::SinCos(angle, sina, cosa); + m.Mul(cosa, sina, -sina, cosa); + } + 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); + } + 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); + while(i + 1 < n) { + int cw = ff[s[i]]; + if(dx && dx[i] != cw) + break; + posx += cw; + CharPos np = GetCharPos(fnt, s[i + 1]); + if(np.fi != fp.fi) + break; + page << FormatIntHex(np.ci, 2); + i++; + } + page << "> Tj\n"; + posx += dx ? dx[i] : ff[s[i]]; + prev = next; + } + } + else { + page << Pt(x) << " " << Pt(pgsz.cy - y - ff.GetAscent()) << " Td\n"; + int fi = -1; + int ppos = 0; + int np = 0; + for(int i = 0; i < n; i++) { + CharPos fp = GetCharPos(fnt, s[i]); + if(fp.fi != fi) { + FlushText(np, fi, fnt.GetHeight(), txt); + txt.Clear(); + np = posx - ppos; + ppos = posx; + fi = fp.fi; + } + txt.Cat(Sprintf("%02.2X", fp.ci)); + posx += ff[s[i]]; + } + FlushText(np, fi, fh, txt); + } + page << "ET\n"; + if(fnt.IsUnderline()) { + int w = ff.GetAscent() / 15; + int dy = ff.GetAscent() + max((ff.GetDescent() - w) / 2, ff.GetAscent() / 10); + DrawLine(fround(x + sina * dy), + fround(y + cosa * dy), + fround(x + cosa * posx + sina * dy), + fround(y + cosa * dy - sina * posx), w, ink); + } +} + +Image RenderGlyph(int cx, int x, Font font, int chr, int py, int pcy) +{ + ImageDraw iw(cx, pcy); + iw.DrawRect(0, 0, cx, pcy, White); + iw.DrawText(x, -py, WString(chr, 1), font, Black); + return iw; +} + +PdfDraw::RGlyph PdfDraw::RasterGlyph(Font fnt, int chr) +{ + RGlyph rg; + FontInfo fi = fnt.Info(); + rg.x = 0; + rg.sz.cx = fi[chr]; + rg.sz.cy = fi.GetHeight(); + int l = fi.GetLeftSpace(chr); + if(l < 0) { + rg.x = -l; + rg.sz.cx -= l; + } + int r = fi.GetRightSpace(chr); + if(r < 0) + rg.sz.cx -= r; + RasterFormat fmt; + fmt.Set1mf(); + int linebytes = fmt.GetByteCount(rg.sz.cx); + Buffer ob(linebytes); + int y = 0; + while(y < rg.sz.cy) { + int ccy = min(16, rg.sz.cy - y); + Image m = RenderGlyph(rg.sz.cx, rg.x, fnt, chr, y, ccy); + for(int i = 0; i < m.GetHeight(); i++) { + fmt.Write(ob, m[i], rg.sz.cx, NULL); + rg.data.Cat((const char *)~ob, linebytes); + } + y += ccy; + } + return rg; +} + +void PdfDraw::DrawRectOp(int x, int y, int cx, int cy, Color color) +{ + if(IsNull(color) || cx <= 0 || cy <= 0) return; + PutrgColor(color); + PutRGColor(color); + PutRect(RectC(x, y, cx, cy)); + page << "f\n"; +} + +void PdfDraw::DrawLineOp(int x1, int y1, int x2, int y2, int width, Color color) +{ + if(!IsNull(width)) { + PutRGColor(color); + PutLineWidth(width); + page << " 1 J " + << Pt(x1) << ' ' << Pt(pgsz.cy - y1) << " m " + << Pt(x2) << ' ' << Pt(pgsz.cy - y2) << " l S\n"; + } +} + +void PdfDraw::DrawImageOp(int x, int y, int cx, int cy, const Image& _img, const Rect& src, Color c) +{ + Image img = _img; + if(!IsNull(c)) + img = SetColorKeepAlpha(img, c); + image.Add(img); + imagerect.Add(src); + page << "q " + << Pt(cx) << " 0 0 " << Pt(cy) << ' ' + << Pt(x) << ' ' << Pt(pgsz.cy - y - cy) + << " cm /Image" << image.GetCount() << " Do Q\n"; +} + +void PdfDraw::DrawPolyPolylineOp(const Point *vertices, int vertex_count, + const int *counts, int count_count, + int width, Color color, Color doxor) +{ + if(IsNull(color) || IsNull(width)) + return; + PutRGColor(color); + PutLineWidth(width); + while(--count_count >= 0) { + int part = *counts++; + page << Pt(vertices->x) << ' ' << Pt(pgsz.cy - vertices->y) << " m\n"; + vertices++; + for(; --part > 0; vertices++) + page << Pt(vertices->x) << ' ' << Pt(pgsz.cy - vertices->y) << " l\n"; + page << "S\n"; + } +} + +void PdfDraw::DrawEllipseOp(const Rect& r, Color color, int pen, Color outline) +{ + bool fill = !IsNull(color), stroke = !IsNull(outline) && !IsNull(pen); + if(fill) PutrgColor(color); + if(stroke) { PutRGColor(outline); PutLineWidth(pen); } + if(!fill && !stroke) + return; + char closeop = (stroke && fill ? 'B' : fill ? 'f' : 'S'); + int sizelim = r.Width() | r.Height(); + int bits = 1; + while(bits < 20 && sizelim > (1 << bits)) + bits++; + int parts = 3 + max(((bits * (bits - 6)) >> 3) | 1, 1); + double k = 2 * M_PI / 3 / parts; + Pointf center(Pt((r.left + r.right) / 2.0), Pt(pgsz.cy - ((r.top + r.bottom) / 2.0))); + Pointf size(Pt(r.Width() / 2.0), Pt(r.Height() / 2.0)); + for(int partid = 0; partid < parts; partid++) { + double phi1 = (partid + 0) * (2 * M_PI / parts); + double phi2 = (partid + 1) * (2 * M_PI / parts); + double s1 = sin(phi1), c1 = cos(phi1), s2 = sin(phi2), c2 = cos(phi2); + Pointf A = center + Pointf(c1, s1) * size; + Pointf D = center + Pointf(c2, s2) * size; + Pointf dA = Pointf(-s1, c1) * size; + Pointf dD = Pointf(-s2, c2) * size; + Pointf B = A + k * dA; + Pointf C = D - k * dD; + if(!partid) + page << FormatDoubleFix(A.x, 2) << ' ' << FormatDoubleFix(A.y, 2) << " m\n"; + page << FormatDoubleFix(B.x, 2) << ' ' << FormatDoubleFix(B.y, 2) << ' ' + << FormatDoubleFix(C.x, 2) << ' ' << FormatDoubleFix(C.y, 2) << ' ' + << FormatDoubleFix(D.x, 2) << ' ' << FormatDoubleFix(D.y, 2) << " c\n"; + } + page << closeop << '\n'; +} + +void PdfDraw::DrawArcOp(const Rect& rc, Point start, Point end, int width, Color color) +{ + //TODO!! + NEVER(); +} + +void PdfDraw::DrawPolyPolyPolygonOp(const Point *vertices, int vertex_count, + const int *subpolygon_counts, int subpolygon_count_count, + const int *disjunct_polygon_counts, int disjunct_polygon_count_count, + Color color, int width, Color outline, uint64 pattern, Color doxor) +{ + bool fill = !IsNull(color), stroke = !IsNull(outline) && !IsNull(width); + if(fill) PutrgColor(color); + if(stroke) { PutRGColor(outline); PutLineWidth(width); } + if(!fill && !stroke) return; + const char *closeop = (fill & stroke ? "B*" : fill ? "f*" : "S"); + while(--disjunct_polygon_count_count >= 0) { + int disj = *disjunct_polygon_counts++; + while(disj > 0) { + int sub = *subpolygon_counts++; + disj -= sub; + page << Pt(vertices->x) << ' ' << Pt(pgsz.cy - vertices->y) << " m\n"; + vertices++; + for(; --sub > 0; vertices++) + page << Pt(vertices->x) << ' ' << Pt(pgsz.cy - vertices->y) << " l\n"; + page << "h\n"; + } + page << closeop << '\n'; + } +} + +String PdfDraw::Finish() +{ + if(!IsNull(page)) + PutStream(page); + + int pagecount = offset.GetCount(); + + Vector imageobj; + for(int i = 0; i < image.GetCount(); i++) { + Size sz = image[i].GetSize(); + Rect sr = sz & imagerect[i]; + String data; + const Image& m = image[i]; + int mask = -1; + int smask = -1; + if(m.GetKind() == IMAGE_MASK) { + for(int y = sr.top; y < sr.bottom; y++) { + const RGBA *p = m[y] + sr.left; + const RGBA *e = m[y] + sr.right; + while(p < e) { + int bit = 0x80; + byte b = 0; + while(bit && p < e) { + if(p->a != 255) + b |= bit; + bit >>= 1; + p++; + } + data.Cat(b); + } + } + mask = PutStream(data, String().Cat() + << "/Type /XObject /Subtype /Image /Width " << sr.Width() + << " /Height " << sr.Height() + << " /BitsPerComponent 1 /ImageMask true /Decode [0 1]"); + } + if(m.GetKind() == IMAGE_ALPHA) { + for(int y = sr.top; y < sr.bottom; y++) { + const RGBA *p = m[y] + sr.left; + const RGBA *e = m[y] + sr.right; + while(p < e) + data.Cat((p++)->a); + } + smask = PutStream(data, String().Cat() + << "/Type /XObject /Subtype /Image /Width " << sr.Width() + << " /Height " << sr.Height() + << " /BitsPerComponent 8 /ColorSpace /DeviceGray /Decode [0 1]"); + } + data.Clear(); + for(int y = sr.top; y < sr.bottom; y++) { + const RGBA *p = m[y] + sr.left; + const RGBA *e = m[y] + sr.right; + while(p < e) { + data.Cat(p->r); + data.Cat(p->g); + data.Cat(p->b); + p++; + } + } + String imgobj; + imgobj << "/Type /XObject /Subtype /Image /Width " << sr.Width() + << " /Height " << sr.Height() << " /BitsPerComponent 8 /ColorSpace /DeviceRGB"; + if(mask >= 0) + imgobj << " /Mask " << mask << " 0 R"; + if(smask >= 0) + imgobj << " /SMask " << smask << " 0 R"; + imageobj << PutStream(data, imgobj); + } + +/* + Vector rgobj; + Vector rgsz; + Vector rgx; + for(int i = 0; i < pdffont.GetCount(); i++) { + Font fnt = pdffont.GetKey(i); + if(fnt.GetHeight()) { + for(int c = 0; c < pdffont[i].GetCount(); c++) { + RGlyph rg = RasterGlyph(fnt, pdffont[i][c]); + int ii = rgobj.GetCount(); + rgobj << PutStream(rg.data, String().Cat() + << "/Type /XObject /Subtype /Image " + << " /Width " << rg.sz.cx << " /Height " << rg.sz.cy + << " /BitsPerComponent 1 /ImageMask true /Decode [0 1]"); + rgsz.Add(rg.sz); + rgx.Add(rg.x); + } + } + } +*/ + Vector fontobj; + for(int i = 0; i < pdffont.GetCount(); i++) { + Font fnt = pdffont.GetKey(i); + const Vector& cs = pdffont[i]; + String cmap; + cmap << + "/CIDInit /ProcSet findresource begin\n" + "12 dict begin\n" + "begincmap\n" + "/CIDSystemInfo\n" + "<< /Registry (Adobe)\n" + "/Ordering (UCS)\n" + "/Supplement 0\n" + ">> def\n" + "/CMapName /UCS" << i << " def\n" + "/CMapType 2 def\n" + "1 begincodespacerange\n" + "<00> <" << FormatIntHex(cs.GetCount() - 1, 2) << ">\n" + "endcodespacerange\n" + "1 beginbfrange\n" + "<00> <" << FormatIntHex(cs.GetCount() - 1, 2) << ">\n" + "[\n"; + for(int c = 0; c < cs.GetCount(); c++) + cmap << '<' << FormatIntHex(cs[c], 4) << ">\n"; + cmap << + "]\n" + "endbfrange\n" + "endcmap\n" + "CMapName currentdict /CMap defineresource pop\n" + "end\n" + "end\n"; + int cmapi = PutStream(cmap); + if(fnt.GetHeight()) { + FontInfo fi = fnt.Info(); + int t3ch = offset.GetCount() + 1; + int fa = fi.GetHeight() - fi.GetInternal(); + String res; + for(int c = 0; c < cs.GetCount(); c++) { + RGlyph rg = RasterGlyph(fnt, pdffont[i][c]); + String proc; + proc + << 1000 * fi[cs[c]] / fa << " 0 0 " + << -1000 * fi.GetDescent() / fa << ' ' + << 1000 * (rg.sz.cx + rg.x) / fa << ' ' + << 1000 * rg.sz.cy / fa + << " d1\nq " + << 1000 * rg.sz.cx / fa + << " 0 0 " + << 1000 * rg.sz.cy / fa + << " " + << -1000 * rg.x / fa + << " " + << -1000 * fi.GetDescent() / fa + << " cm BI /W " << rg.sz.cx << " /H " << rg.sz.cy + << " /BPC 1 /IM true /D [0 1] ID\n" + << rg.data + << "\nEI Q" + ; + PutStream(proc); + } + int charprocs = BeginObj(); + out << "<<"; + for(int c = 0; c < cs.GetCount(); c++) + out << " /Rgch" << c << ' ' << t3ch + c<< " 0 R"; + out << " >>\n"; + EndObj(); + int encoding = BeginObj(); + out << "<< /Type /Encoding /Differences [0"; + for(int c = 0; c < cs.GetCount(); c++) + out << " /Rgch" << c; + out << "] >>\n"; + EndObj(); + fontobj.Add() = BeginObj(); + out << + "<< /Name /F" << i + 1 << + " /Type /Font\n" + "/Subtype /Type3\n" + "/FontBBox [0 0 0 0]\n" + "/FontMatrix [0.001 0 0 0.001 0 0]\n" + "/CharProcs " << charprocs << " 0 R\n" + "/Encoding " << encoding << " 0 R\n" + "/FirstChar 0\n" + "/LastChar " << cs.GetCount() - 1 << "\n" + "/Widths ["; + for(int i = 0; i < cs.GetCount(); i++) + out << ' ' << 1000 * fi[cs[i]] / fa; + out << + "]\n"; + out << "/Resources << /ProcSet [ /PDF /Text /ImageB ] >>\n" + << "/FirstChar 0 /LastChar " << cs.GetCount() - 1 <<" /ToUnicode " + << cmapi + << " 0 R\n>>\n"; + EndObj(); + } + else { + #ifdef PLATFORM_POSIX + FontInfo fi = pdffont.GetKey(i).Info(); + String fontbuffer = LoadFile(fi.GetFileName()); + #endif + #ifdef PLATFORM_WIN32 + ScreenDraw sd; + sd.SetFont(pdffont.GetKey(i)); + + int size = GetFontData(sd.GetHandle(), 0, 0, NULL, 0); + if(size == GDI_ERROR) { + LLOG("PdfDraw::Finish: GDI_ERROR on font " << pdffont.GetKey(i)); + return Null; + } + StringBuffer fontbuffer(size); + GetFontData(sd.GetHandle(), 0, 0, fontbuffer, size); + #endif + + TTFReader ttf; + if(!ttf.Open(fontbuffer)) + return Null; + + + String name = FormatIntAlpha(i + 1, true); + name.Cat('A', 6 - name.GetLength()); + name << '+' << ttf.ps_name; + + int fonti = PutStream(ttf.Subset(cs)); + + BeginObj(); + int ascent = ttf.hhea.ascent * 1000 / ttf.head.unitsPerEm; + int descent = ttf.hhea.descent * 1000 / ttf.head.unitsPerEm; + out << + "<< /Type /FontDescriptor\n" + "/FontName /" << name << "\n" + "/Flags 4\n" + "/FontBBox [ -1000 " << descent << " 3000 " << ascent << " ]\n" //????? + "/ItalicAngle " << ttf.post.italicAngle / 65536.0 << "\n" + "/Ascent " << ascent << "\n" + "/Descent " << -descent << "\n" + "/CapHeight " << ttf.hhea.ascent * 1000 / ttf.head.unitsPerEm << "\n" + "/StemV 80\n" + "/FontFile2 " << fonti << " 0 R\n" << + ">>\n"; + EndObj(); + + fontobj.Add() = BeginObj(); + out << + "<< /Type /Font\n" + "/Subtype /TrueType\n" + "/BaseFont /" << name << "\n" + "/FirstChar 0\n" + "/LastChar " << cs.GetCount() - 1 << "\n" + "/Widths [ "; + for(int i = 0; i < cs.GetCount(); i++) + out << ttf.GetAdvanceWidth(cs[i]) * 1000 / ttf.head.unitsPerEm << ' '; + out << + "]\n" + "/FontDescriptor " << fonti + 1 << " 0 R\n" + "/ToUnicode " << cmapi << " 0 R\n" << + ">>\n"; + EndObj(); + } + } + + int pages = BeginObj(); + out << "<< /Type /Pages\n" + << "/Kids ["; + for(int i = 0; i < pagecount; i++) + out << i + pages + 1 << " 0 R "; + out << "]\n" + << "/Count " << pagecount << "\n" + << "/Resources << /ProcSet [ /PDF /Text /ImageB /ImageC ]\n"; + if(pdffont.GetCount()) { + out << "/Font << "; + for(int i = 0; i < pdffont.GetCount(); i++) + out << "/F" << i + 1 << ' ' << fontobj[i] << " 0 R "; + out << ">>\n"; + } + if(!imageobj.IsEmpty()) { + out << "/XObject << "; + for(int i = 0; i < imageobj.GetCount(); i++) + out << "/Image" << i + 1 << ' ' << imageobj[i] << " 0 R "; + out << ">>\n"; + } + out << ">> >>\n"; + EndObj(); + for(int i = 0; i < pagecount; i++) { + BeginObj(); + out << "<< /Type /Page\n" + << "/Parent " << pages << " 0 R\n" + << "/MediaBox [0 0 " << Pt(pgsz.cx) << ' ' << Pt(pgsz.cy) << "]\n" + << "/Contents " << i + 1 << " 0 R\n" + << ">>\n"; + EndObj(); + } + int outlines = BeginObj(); + out << "<< /Type /Outlines\n" + "/Count 0\n" + ">>\n"; + EndObj(); + int catalog = BeginObj(); + out << "<< /Type /Catalog\n" + << "/Outlines " << outlines << " 0 R\n" + << "/Pages " << pages << " 0 R\n" + << ">>\n"; + EndObj(); + int startxref = out.GetCount(); + out << "xref\n" + << "0 " << offset.GetCount() + 1 << "\n"; + out << "0000000000 65535 f\r\n"; + for(int i = 0; i < offset.GetCount(); i++) + out << Sprintf("%010d 00000 n\r\n", offset[i]); + out << "\n" + << "trailer\n" + << "<< /Size " << offset.GetCount() + 1 << "\n" + << "/Root " << catalog << " 0 R\n" + << ">>\n" + << "startxref\r\n" + << startxref << "\r\n" + << "%%EOF\r\n"; + return out; +} + +END_UPP_NAMESPACE diff --git a/uppsrc/PdfDraw/PdfDraw.h b/uppsrc/PdfDraw/PdfDraw.h new file mode 100644 index 000000000..98876e751 --- /dev/null +++ b/uppsrc/PdfDraw/PdfDraw.h @@ -0,0 +1,343 @@ +#ifndef _PDF_pdf_h_ +#define _PDF_pdf_h_ + +#include +#include + + +NAMESPACE_UPP + +class TTFReader { + struct TTFStream { + struct Fail {}; + + virtual void Raw(void *ptr, int count) = 0; + + TTFStream& operator%(byte& q); + TTFStream& operator%(int32& q); + TTFStream& operator%(uint32& q); + TTFStream& operator%(int16& q); + TTFStream& operator%(uint16& q); + + virtual ~TTFStream() {} + }; + + struct TTFStreamIn : TTFStream { + const char *beg; + const char *s; + const char *lim; + + virtual void Raw(void *ptr, int count); + + void Seek(int offset); + int Get8(); + int Get16(); + int Get32(); + String Get(int n); + + virtual ~TTFStreamIn() {} + }; + + struct TTFStreamOut : TTFStream { + String out; + + virtual void Raw(void *ptr, int count) { out.Cat((const char *)ptr, count); } + + void Put8(int data) { out.Cat(data); } + void Put16(int data) { Put8(HIBYTE(data)); Put8(LOBYTE(data)); } + void Put32(int data) { Put16(HIWORD(data)); Put16(LOWORD(data)); } + void Put(const String& s) { out.Cat(s); } + + operator String() const { return out; } + + virtual ~TTFStreamOut() {} + }; + + String font; + + struct Table : Moveable { + int offset; + int length; + }; + VectorMap table; + + word zero[256]; + word *cmap[256]; + + struct GlyphInfo : Moveable { + int offset; + int size; + uint16 advanceWidth; + int16 leftSideBearing; + }; + + Vector glyphinfo; + + void SetGlyph(wchar ch, word glyph); + + struct Fail {}; + + static void Error() { throw Fail(); } + + int Peek8(const char *s); + int Peek16(const char *s); + int Peek32(const char *s); + int Peek8(const char *s, int i); + int Peek16(const char *s, int i); + int Peek32(const char *s, int i); + int Read8(const char *&s); + int Read16(const char *&s); + int Read32(const char *&s); + String Read(const char *&s, int n); + + void Free(); + void Reset(); + + const char *Seek(const char *tab, int& len); + const char *Seek(const char *tab); + void Seek(const char *tab, TTFStreamIn& s); + String GetTable(const char *tab); + + enum { + ARG_1_AND_2_ARE_WORDS = (1<<0), + ARGS_ARE_XY_VALUES = (1<<1), + ROUND_XY_TO_GRID = (1<<2), + WE_HAVE_A_SCALE = (1<<3), + MORE_COMPONENTS = (1<<5), + WE_HAVE_AN_X_AND_Y_SCALE = (1<<6), + WE_HAVE_A_TWO_BY_TWO = (1<<7), + WE_HAVE_INSTRUCTIONS = (1<<8), + USE_MY_METRICS = (1<<9), + }; + + struct NewTable { + String name; + String data; + dword checksum; + }; + + static void Add(Array& t, const char *name, const String& data); + static void Set(NewTable& m, const String& data); + static String CreateHeader(const Array& t); + + static inline int Copy8(TTFStreamOut& out, TTFStreamIn& in) + { int q = in.Get8(); out.Put8(q); return q; } + static inline int Copy16(TTFStreamOut& out, TTFStreamIn& in) + { int q = in.Get16(); out.Put16(q); return q; } + static inline int Copy32(TTFStreamOut& out, TTFStreamIn& in) + { int q = in.Get32(); out.Put32(q); return q; } + static inline String Copy(TTFStreamOut& out, TTFStreamIn& in, int n) + { String q = in.Get(n); out.Put(q); return q; } + +public: + struct Head { + dword version; + dword fontRevision; + uint32 checkSumAdjustment; + uint32 magicNumber; + uint16 flags; + uint16 unitsPerEm; + byte created[8]; + byte modified[8]; + int16 xMin; + int16 yMin; + int16 xMax; + int16 yMax; + uint16 macStyle; + uint16 lowestRecPPEM; + int16 fontDirectionHint; + int16 indexToLocFormat; + int16 glyphDataFormat; + + void Serialize(TTFStream& s); + }; + + struct Hhea { + uint32 version; + int16 ascent; + int16 descent; + int16 lineGap; + uint16 advanceWidthMax; + int16 minLeftSideBearing; + int16 minRightSideBearing; + int16 xMaxExtent; + int16 caretSlopeRise; + int16 caretSlopeRun; + int16 caretOffset; + int16 reserved1; + int16 reserved2; + int16 reserved3; + int16 reserved4; + int16 metricDataFormat; + uint16 numOfLongHorMetrics; + + void Serialize(TTFStream& s); + }; + + struct Maxp { + uint32 version; + uint16 numGlyphs; + uint16 maxPoints; + uint16 maxContours; + uint16 maxComponentPoints; + uint16 maxComponentContours; + uint16 maxZones; + uint16 maxTwilightPoints; + uint16 maxStorage; + uint16 maxFunctionDefs; + uint16 maxInstructionDefs; + uint16 maxStackElements; + uint16 maxSizeOfInstructions; + uint16 maxComponentElements; + uint16 maxComponentDepth; + + void Serialize(TTFStream& s); + }; + + struct Post { + uint32 format; + int32 italicAngle; + int16 underlinePosition; + int16 underlineThickness; + uint16 isFixedPitch; + uint16 reserved; + uint32 minMemType42; + uint32 maxMemType42; + uint32 minMemType1; + uint32 maxMemType1; + + void Serialize(TTFStream& s); + }; + + Head head; + Hhea hhea; + Maxp maxp; + Post post; + String ps_name; + + int GetGlyph(wchar chr) { return cmap[HIBYTE(chr)][LOBYTE(chr)]; } + word GetAdvanceWidth(wchar chr) { return glyphinfo[GetGlyph(chr)].advanceWidth; } + + String Subset(const Vector& chars, int first = 0, bool os2 = false); + bool Open(const String& fnt, bool symbol = false, bool justcheck = false); + + TTFReader(); + ~TTFReader(); +}; + +class PdfDraw : public Draw { +public: + virtual dword GetInfo() const; + virtual Size GetPageSize() const; + + virtual void StartPage(); + virtual void EndPage(); + + virtual void BeginOp(); + virtual void EndOp(); + virtual void OffsetOp(Point p); + virtual bool ClipOp(const Rect& r); + virtual bool ClipoffOp(const Rect& r); + virtual bool ExcludeClipOp(const Rect& r); + virtual bool IntersectClipOp(const Rect& r); + virtual bool IsPaintingOp(const Rect& r) const; + + virtual void DrawRectOp(int x, int y, int cx, int cy, Color color); + virtual void DrawImageOp(int x, int y, int cx, int cy, const Image& img, const Rect& src, Color color); + virtual void DrawLineOp(int x1, int y1, int x2, int y2, int width, Color color); + virtual void DrawPolyPolylineOp(const Point *vertices, int vertex_count, + const int *counts, int count_count, + int width, Color color, Color doxor); + virtual void DrawPolyPolyPolygonOp(const Point *vertices, int vertex_count, + const int *subpolygon_counts, int scc, + const int *disjunct_polygon_counts, int dpcc, + Color color, int width, Color outline, + uint64 pattern, Color doxor); + virtual void DrawEllipseOp(const Rect& r, Color color, int pen, Color pencolor); + virtual void DrawArcOp(const Rect& rc, Point start, Point end, int width, Color color); + virtual void DrawTextOp(int x, int y, int angle, const wchar *text, Font font, + Color ink, int n, const int *dx); + +private: + struct CharPos : Moveable { word fi, ci; }; + + struct OutlineInfo : Moveable { + bool ttf; + bool sitalic; + }; + + VectorMap outline_info; + VectorMap > pdffont; + VectorMap > fontchars; + Vector image; + Vector imagerect; + + Vector offset; + String out; + String page; + Size pgsz; + Color rgcolor; + Color RGcolor; + int fontid; + double textht; + double linewidth; + int margin; + + inline double Pt(double dot) { return 0.12 * dot; } + + int Pos() { return offset.GetCount() + 1; } + int BeginObj(); + void EndObj(); + int PutStream(const String& data, const String& keys = Null); + + void PutRect(const Rect& rc); + void PutrgColor(Color rg); + void PutRGColor(Color RG); + void PutFontHeight(int fi, double ht); + void PutLineWidth(int lw); + + CharPos GetCharPos(Font fnt, wchar chr); + void FlushText(int dx, int fi, int height, const String& txt); + static String PdfColor(Color c); + + 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); + + struct RGlyph : Moveable { + String data; + Size sz; + int x; + }; + + RGlyph RasterGlyph(Font fnt, int chr); + +public: + String Finish(); + void Clear(); + + PdfDraw(int pagecx, int pagecy) { Init(pagecx, pagecy, 0); } + PdfDraw(Size pgsz = Size(5100, 6600)) { Init(pgsz.cx, pgsz.cy, 0); } +}; + +String Pdf(const Array& report, Size pagesize, int margin); + +END_UPP_NAMESPACE + +#endif diff --git a/uppsrc/PdfDraw/PdfDraw.upp b/uppsrc/PdfDraw/PdfDraw.upp new file mode 100644 index 000000000..179134a44 --- /dev/null +++ b/uppsrc/PdfDraw/PdfDraw.upp @@ -0,0 +1,16 @@ +description "PDF output as Draw derived class\377128,0,255"; + +uses + Draw; + +file + PdfDraw.h, + TTFStream.cpp, + TTFStruct.cpp, + TTFReader.cpp, + TTFSubset.cpp, + PdfDraw.cpp, + PdfReport.icpp, + Info readonly separator, + Copying; + diff --git a/uppsrc/PdfDraw/PdfReport.icpp b/uppsrc/PdfDraw/PdfReport.icpp new file mode 100644 index 000000000..d20aa0ecd --- /dev/null +++ b/uppsrc/PdfDraw/PdfReport.icpp @@ -0,0 +1,21 @@ +#include "PdfDraw.h" + +NAMESPACE_UPP + +String Pdf(const Array& report, Size sz, int margin) +{ + PdfDraw w(sz + 2 * margin); + for(int i = 0; i < report.GetCount(); i++) { + w.StartPage(); + w.DrawDrawing(margin, margin, sz.cx, sz.cy, report[i]); + w.EndPage(); + } + return w.Finish(); +} + +INITBLOCK +{ + SetDrawingToPdfFn(Pdf); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/PdfDraw/TTFReader.cpp b/uppsrc/PdfDraw/TTFReader.cpp new file mode 100644 index 000000000..7c6a0a27d --- /dev/null +++ b/uppsrc/PdfDraw/TTFReader.cpp @@ -0,0 +1,311 @@ +#include "PdfDraw.h" + +NAMESPACE_UPP + +#define LLOG(x) // LOG(x) +#define LDUMP(x) // LLOG(#x << " = " << x); + +int TTFReader::Peek8(const char *s) +{ + if(s + 1 > font.End()) + Error(); + return (byte)*s; +} + +int TTFReader::Peek16(const char *s) +{ + if(s + 2 > font.End()) + Error(); + return ((byte)s[0] << 8) | (byte)s[1]; +} + +int TTFReader::Peek32(const char *s) +{ + if(s + 4 > font.End()) + Error(); + return ((byte)s[0] << 24) | ((byte)s[1] << 16) | ((byte)s[2] << 8) | (byte)s[3]; +} + +int TTFReader::Peek8(const char *s, int i) +{ + return Peek16(s + i); +} + +int TTFReader::Peek16(const char *s, int i) +{ + return Peek16(s + 2 * i); +} + +int TTFReader::Peek32(const char *s, int i) +{ + return Peek32(s + 4 * i); +} + +int TTFReader::Read8(const char *&s) +{ + int q = byte(*s); s++; return q; +} + +int TTFReader::Read16(const char *&s) +{ + int q = Peek16(s); s += 2; return q; +} + +int TTFReader::Read32(const char *&s) +{ + int q = Peek32(s); s += 4; return q; +} + +String TTFReader::Read(const char *&s, int n) +{ + if(s + n > font.End()) + Error(); + String q(s, n); + s += n; + return q; +} + +void TTFReader::Reset() +{ + memset(zero, 0, sizeof(zero)); + for(int i = 0; i < 256; i++) + cmap[i] = zero; +} + +void TTFReader::Free() +{ + for(int i = 0; i < 256; i++) + if(cmap[i] != zero) + delete[] cmap[i]; +} + +void TTFReader::SetGlyph(wchar chr, word glyph) +{ + int h = HIBYTE(chr); + if(cmap[h] == zero) + memset(cmap[h] = new word[256], 0, 256 * sizeof(word)); + cmap[h][LOBYTE(chr)] = glyph; +} + +const char *TTFReader::Seek(const char *tab, int& len) +{ + ASSERT(strlen(tab) == 4); + int q = table.Find(tab); + if(q < 0) + Error(); + len = table[q].length; + return ~font + table[q].offset; +} + +const char *TTFReader::Seek(const char *tab) +{ + int dummy; + return Seek(tab, dummy); +} + +void TTFReader::Seek(const char *tab, TTFStreamIn& s) +{ + int len; + s.beg = s.s = Seek(tab, len); + s.lim = s.s + len; +} + +String TTFReader::GetTable(const char *tab) +{ + ASSERT(strlen(tab) == 4); + int q = table.Find(tab); + if(q < 0) + return Null; + return String(~font + table[q].offset, table[q].length); +} + +bool TTFReader::Open(const String& fnt, bool symbol, bool justcheck) +{ + try { + int i; + Free(); + Reset(); + table.Clear(); + glyphinfo.Clear(); + font = fnt; + const char *s = fnt; + int q = Read32(s); + if(q != 0x74727565 && q != 0x00010000) + Error(); + int n = Read16(s); + s += 6; + while(n--) { + Table& t = table.Add(Read(s, 4)); + s += 4; + t.offset = Read32(s); + t.length = Read32(s); + } + for(i = 0; i < table.GetCount(); i++) + LLOG("table: " << table.GetKey(i) << " offset: " << table[i].offset << " length: " << table[i].length); + + TTFStreamIn is; + Seek("head", is); + head.Serialize(is); + if(head.magicNumber != 0x5F0F3CF5) + Error(); + + LDUMP(head.unitsPerEm); + LDUMP(head.xMin); + LDUMP(head.yMin); + LDUMP(head.xMax); + LDUMP(head.yMax); + LDUMP(head.indexToLocFormat); + + Seek("maxp", is); + maxp.Serialize(is); + LDUMP(maxp.numGlyphs); + + Seek("post", is); + post.Serialize(is); +// LLOGHEXLDUMP(is.s, is.lim - is.s); + LDUMP((post.format >> 16)); + LDUMP(post.italicAngle); + LDUMP(post.italicAngle); + LDUMP(post.underlinePosition); + LDUMP(post.underlineThickness); + LDUMP(post.italicAngle); + + if(justcheck) + return true; + + Seek("hhea", is); + hhea.Serialize(is); + LDUMP(hhea.ascent); + LDUMP(hhea.descent); + LDUMP(hhea.lineGap); + LDUMP(hhea.advanceWidthMax); + LDUMP(hhea.numOfLongHorMetrics); + + if(hhea.numOfLongHorMetrics > maxp.numGlyphs) + Error(); + + glyphinfo.SetCount(maxp.numGlyphs); + + s = Seek("hmtx"); + int aw = 0; + for(i = 0; i < hhea.numOfLongHorMetrics; i++) { + aw = glyphinfo[i].advanceWidth = (uint16)Read16(s); + glyphinfo[i].leftSideBearing = (int16)Read16(s); + } + for(; i < maxp.numGlyphs; i++) { + glyphinfo[i].advanceWidth = aw; + glyphinfo[i].leftSideBearing = (int16)Read16(s); + } + + s = Seek("loca"); + for(i = 0; i < maxp.numGlyphs; i++) + if(head.indexToLocFormat) { + glyphinfo[i].offset = Peek32(s, i); + glyphinfo[i].size = Peek32(s, i + 1) - glyphinfo[i].offset; + } + else { + glyphinfo[i].offset = 2 * (word)Peek16(s, i); + glyphinfo[i].size = 2 * (word)Peek16(s, i + 1) - glyphinfo[i].offset; + } + + for(i = 0; i < maxp.numGlyphs; i++) + LLOG(i << " advance: " << glyphinfo[i].advanceWidth << ", left: " << glyphinfo[i].leftSideBearing + << ", offset: " << glyphinfo[i].offset << ", size: " << glyphinfo[i].size); + + s = Seek("cmap"); + const char *p = s; + p += 2; + n = Read16(p); + while(n--) { + int pid = Read16(p); + int psid = Read16(p); + int offset = Read32(p); + LLOG("cmap pid: " << pid << " psid: " << psid << " format: " << Peek16(s + offset)); + //Test with Symbol font !!!; improve - Unicode first, 256 bytes later..., symbol... + if(symbol) { + if(pid == 1 && psid == 0 && Peek16(s + offset) == 0) { + LLOG("Reading symbol table"); + p = s + offset + 6; + for(int i = 0; i < 256; i++) + SetGlyph(i, (byte)p[i]); + break; + } + } + else + if(pid == 3 && psid == 1) { + p = s + offset; + n = Peek16(p + 6) >> 1; + LLOG("Found UNICODE encoding, offset " << offset << ", segments " << n); + const char *seg_end = p + 14; + const char *seg_start = seg_end + 2 * n + 2; + const char *idDelta = seg_start + 2 * n; + const char *idRangeOffset = idDelta + 2 * n; + for(int i = 0; i < n; i++) { + int start = Peek16(seg_start, i); + int end = Peek16(seg_end, i); + int delta = Peek16(idDelta, i); + int ro = Peek16(idRangeOffset, i); + if (ro && delta == 0) { + LLOG("RangeOffset start: " << start << ", end: " << end << ", delta: " << (int16)delta); + LLOG("ro " << ro); + const char *q = idRangeOffset + 2 * i + ro; + for(int c = start; c <= end; c++) { + SetGlyph(c, (word)Read16(q)); + } + } + else { + LLOG("start: " << start << ", end: " << end << ", delta: " << (int16)delta); + for(int c = start; c <= end; c++) + SetGlyph(c, c + delta); + } + } + break; + } + } + + + const char *strings = Seek("name"); + s = strings + 2; + int count = Read16(s); + strings += (word)Read16(s); + for(int i = 0; i < count; i++) { + int platform = Read16(s); + s += 4; + if(Read16(s) == 6) { + int len = Read16(s); + int offset = Read16(s); + if(platform == 1) + ps_name = String(strings + offset, len); + else { + s = strings + offset; + len >>= 1; + while(len--) + ps_name.Cat(Read16(s)); + } + break; + } + s += 4; + } + LDUMP(ps_name); + } + catch(Fail) { + return false; + } + catch(TTFStream::Fail) { + return false; + } + + return true; +} + +TTFReader::TTFReader() +{ + Reset(); +} + +TTFReader::~TTFReader() +{ + Free(); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/PdfDraw/TTFStream.cpp b/uppsrc/PdfDraw/TTFStream.cpp new file mode 100644 index 000000000..878587b83 --- /dev/null +++ b/uppsrc/PdfDraw/TTFStream.cpp @@ -0,0 +1,110 @@ +#include "PdfDraw.h" + +NAMESPACE_UPP + +TTFReader::TTFStream& TTFReader::TTFStream::operator%(byte& q) { + Raw(&q, 1); + return *this; +} + +#ifdef CPU_LE +TTFReader::TTFStream& TTFReader::TTFStream::operator%(int32& q) { + byte *h = (byte *)&q; + *this % h[3] % h[2] % h[1] % h[0]; + return *this; +} + +TTFReader::TTFStream& TTFReader::TTFStream::operator%(uint32& q) { + byte *h = (byte *)&q; + *this % h[3] % h[2] % h[1] % h[0]; + return *this; +} + +TTFReader::TTFStream& TTFReader::TTFStream::operator%(int16& q) { + byte *h = (byte *)&q; + *this % h[1] % h[0]; + return *this; +} + +TTFReader::TTFStream& TTFReader::TTFStream::operator%(uint16& q) { + byte *h = (byte *)&q; + *this % h[1] % h[0]; + return *this; +} +#else +TTFReader::TTFStream& TTFReader::TTFStream::operator%(int32& q) { + byte *h = (byte *)&q; + *this % h[0] % h[1] % h[2] % h[3]; + return *this; +} + +TTFReader::TTFStream& TTFReader::TTFStream::operator%(uint32& q) { + byte *h = (byte *)&q; + *this % h[0] % h[1] % h[2] % h[3]; + return *this; +} + +TTFReader::TTFStream& TTFReader::TTFStream::operator%(int16& q) { + byte *h = (byte *)&q; + *this % h[0] % h[1]; + return *this; +} + +TTFReader::TTFStream& TTFReader::TTFStream::operator%(uint16& q) { + byte *h = (byte *)&q; + *this % h[0] % h[1]; + return *this; +} +#endif + +void TTFReader::TTFStreamIn::Seek(int offset) +{ + ASSERT(offset >= 0 && offset < lim - beg); + s = beg + offset; +} + +int TTFReader::TTFStreamIn::Get8() +{ + if(s + 1 > lim) + throw Fail(); + int q = (byte)*s; + s++; + return q; +} + +int TTFReader::TTFStreamIn::Get16() +{ + if(s + 2 > lim) + throw Fail(); + int q = ((byte)s[0] << 8) | (byte)s[1]; + s += 2; + return q; + +} + +int TTFReader::TTFStreamIn::Get32() +{ + if(s + 4 > lim) + throw Fail(); + int q = ((byte)s[0] << 24) | ((byte)s[1] << 16) | ((byte)s[2] << 8) | (byte)s[3]; + s += 2; + return q; +} + +String TTFReader::TTFStreamIn::Get(int n) +{ + if(s + n > lim) + throw Fail(); + String r(s, n); + s += n; + return r; +} + +void TTFReader::TTFStreamIn::Raw(void *ptr, int count) { + if(s + count > lim) + throw Fail(); + memcpy(ptr, s, count); + s += count; +} + +END_UPP_NAMESPACE diff --git a/uppsrc/PdfDraw/TTFStruct.cpp b/uppsrc/PdfDraw/TTFStruct.cpp new file mode 100644 index 000000000..309ddf5f7 --- /dev/null +++ b/uppsrc/PdfDraw/TTFStruct.cpp @@ -0,0 +1,37 @@ +#include "PdfDraw.h" + +NAMESPACE_UPP + +void TTFReader::Head::Serialize(TTFStream& s) +{ + s % version % fontRevision % checkSumAdjustment % magicNumber % flags % unitsPerEm; + s.Raw(created, 8); + s.Raw(modified, 8); + s % xMin % yMin % xMax % yMax % macStyle % lowestRecPPEM % fontDirectionHint + % indexToLocFormat % glyphDataFormat; +} + +void TTFReader::Hhea::Serialize(TTFStream& s) +{ + reserved1 = reserved2 = reserved3 = reserved4 = 0; + s % version % ascent % descent % lineGap % advanceWidthMax % minLeftSideBearing + % minRightSideBearing % xMaxExtent % caretSlopeRise % caretSlopeRun % caretOffset + % reserved1 % reserved2 % reserved3 % reserved4 % metricDataFormat % numOfLongHorMetrics; +} + +void TTFReader::Maxp::Serialize(TTFStream& s) +{ + s % version % numGlyphs % maxPoints % maxContours % maxComponentPoints + % maxComponentContours % maxZones % maxTwilightPoints % maxStorage + % maxFunctionDefs % maxInstructionDefs % maxStackElements + % maxSizeOfInstructions % maxComponentElements % maxComponentDepth; +} + +void TTFReader::Post::Serialize(TTFStream& s) +{ + reserved = 0; + s % format % italicAngle % underlinePosition % underlineThickness % isFixedPitch + % reserved % minMemType42 % maxMemType42 % minMemType1 % maxMemType1; +} + +END_UPP_NAMESPACE diff --git a/uppsrc/PdfDraw/TTFSubset.cpp b/uppsrc/PdfDraw/TTFSubset.cpp new file mode 100644 index 000000000..6164db1eb --- /dev/null +++ b/uppsrc/PdfDraw/TTFSubset.cpp @@ -0,0 +1,207 @@ +#include "PdfDraw.h" + +NAMESPACE_UPP + +dword sCheckSum(const String& data) +{ + const byte *s = (byte *)~data; + const byte *lim = s + (data.GetCount() & ~3); + dword sum = 0; + while(s < lim) { + sum += (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; + s += 4; + } + byte h[4]; + h[0] = h[1] = h[2] = h[3] = 0; + byte *t = h; + while(s < (byte *)data.End()) + *t++ = *s++; + sum += (h[0] << 24) | (h[1] << 16) | (h[2] << 8) | h[3]; + return sum; +} + +void TTFReader::Set(NewTable& m, const String& data) +{ + m.data = data; + m.checksum = sCheckSum(data); +} + +void TTFReader::Add(Array& t, const char *name, const String& data) +{ + if(IsNull(data)) + return; + NewTable& m = t.Add(); + m.name = name; + ASSERT(m.name.GetCount() == 4); + Set(m, data); + m.checksum = sCheckSum(m.data); +} + +String TTFReader::CreateHeader(const Array& t) +{ + TTFStreamOut out; + int i = 1; + int k = 0; + while(i <= t.GetCount()) { + i <<= 1; + k++; + } + out.Put32(0x00010000); + out.Put16(t.GetCount()); + out.Put16(i << 3); + out.Put16(k - 1); + out.Put16((t.GetCount() << 4) - (i << 3)); + int offset = 16 * t.GetCount() + 12; + for(int i = 0; i < t.GetCount(); i++) { + const NewTable& m = t[i]; + out.Put(m.name); + out.Put32(m.checksum); + out.Put32(offset); + out.Put32(m.data.GetLength()); + offset += (m.data.GetLength() + 3) & ~3; + } + return out; +} + +String TTFReader::Subset(const Vector& chars, int first, bool os2) +{ + try { + ASSERT(chars.GetCount() + first < 256); + + TTFStreamOut cmap; + cmap.Put16(0); + cmap.Put16(1); + cmap.Put16(1); + cmap.Put16(0); + cmap.Put32(cmap.out.GetLength() + 4); + cmap.Put16(0); + cmap.Put16(262); + cmap.Put16(0); + + Index glyph; + glyph.Add(0); + for(int i = 0; i < first; i++) + cmap.Put8(0); + for(int i = 0; i < chars.GetCount(); i++) { + int q = GetGlyph(chars[i]); + if(q) { + cmap.Put8(glyph.GetCount()); + glyph.Add(q); + } + else + cmap.Put8(0); + } + for(int i = chars.GetCount(); i < 256; i++) + cmap.Put8(0); + + TTFStreamOut loca; + TTFStreamIn in; + TTFStreamOut glyf; + TTFStreamOut hmtx; + Seek("glyf", in); + for(int i = 0; i < glyph.GetCount(); i++) { + int gi = glyph[i]; + if(gi >= glyphinfo.GetCount()) + throw Fail(); + const GlyphInfo &gf = glyphinfo[gi]; + loca.Put32(glyf.out.GetLength()); + hmtx.Put16(gf.advanceWidth); + hmtx.Put16(gf.leftSideBearing); + if(gf.size) { + if(gf.size < 10) + Error(); + in.Seek(gf.offset); + int c = (int16)Copy16(glyf, in); + Copy(glyf, in, 8); + if(c < 0) { + int flags; + do { + flags = Copy16(glyf, in); + glyf.Put16(glyph.FindAdd(in.Get16())); + if(flags & ARG_1_AND_2_ARE_WORDS) + Copy(glyf, in, 4); + else + Copy(glyf, in, 2); + if(flags & WE_HAVE_A_SCALE) + Copy16(glyf, in); + else + if(flags & WE_HAVE_AN_X_AND_Y_SCALE) + Copy(glyf, in, 4); + else + if(flags & WE_HAVE_A_TWO_BY_TWO) + Copy(glyf, in, 8); + } + while(flags & MORE_COMPONENTS); + if(flags & WE_HAVE_INSTRUCTIONS) + Copy(glyf, in, Copy16(glyf, in)); + } + else + Copy(glyf, in, gf.size - 10); + } + } + loca.Put32(glyf.out.GetLength()); + + Hhea nhhea = hhea; + nhhea.numOfLongHorMetrics = glyph.GetCount(); + TTFStreamOut hhea; + nhhea.Serialize(hhea); + + Maxp nmaxp = maxp; + nmaxp.numGlyphs = glyph.GetCount(); + TTFStreamOut maxp; + nmaxp.Serialize(maxp); + + Post npost = post; + npost.format = 0x00030000; + TTFStreamOut post; + npost.Serialize(post); + + Head nhead = head; + nhead.checkSumAdjustment = 0; + nhead.indexToLocFormat = 1; + TTFStreamOut head; + nhead.Serialize(head); + + Array tab; + if(os2) + Add(tab, "OS/2", GetTable("OS/2")); + Add(tab, "cmap", cmap); + Add(tab, "cvt ", GetTable("cvt ")); + Add(tab, "fpgm", GetTable("fpgm")); + Add(tab, "glyf", glyf); + int headi = tab.GetCount(); + Add(tab, "head", head); + Add(tab, "hhea", hhea); + Add(tab, "hmtx", hmtx); + Add(tab, "loca", loca); + Add(tab, "maxp", maxp); + Add(tab, "name", GetTable("name")); + Add(tab, "post", post); + Add(tab, "prep", GetTable("prep")); + + dword chksum = sCheckSum(CreateHeader(tab)); + for(int i = 0; i < tab.GetCount(); i++) + chksum += tab[i].checksum; + + nhead.checkSumAdjustment = 0xB1B0AFBA - chksum; + head.out.Clear(); + nhead.Serialize(head); + Set(tab[headi], head); + + String result = CreateHeader(tab); + for(int i = 0; i < tab.GetCount(); i++) { + result.Cat(tab[i].data); + result.Cat(0, (4 - result.GetCount()) & 3); + } + + return result; + } + catch(Fail) { + return String::GetVoid(); + } + catch(TTFStream::Fail) { + return String::GetVoid(); + } +} + +END_UPP_NAMESPACE diff --git a/uppsrc/PdfDraw/init b/uppsrc/PdfDraw/init new file mode 100644 index 000000000..d3fcc17d0 --- /dev/null +++ b/uppsrc/PdfDraw/init @@ -0,0 +1,7 @@ +#ifndef _PdfDraw_icpp_init_stub +#define _PdfDraw_icpp_init_stub +#include "Draw/init" +#define BLITZ_INDEX__ F2EB858D15FF91342F7F9648AA934C9C8 +#include "PdfReport.icpp" +#undef BLITZ_INDEX__ +#endif diff --git a/uppsrc/PdfDraw/ttf.h b/uppsrc/PdfDraw/ttf.h new file mode 100644 index 000000000..6e21aed90 --- /dev/null +++ b/uppsrc/PdfDraw/ttf.h @@ -0,0 +1,6 @@ +#ifndef _PDF_ttf_h_ +#define _PDF_ttf_h_ + + + +#endif