31
31
#include "ipeimage.h"
32
32
#include "ipetext.h"
33
#include "ipevisitor.h"
34
33
#include "ipepainter.h"
35
34
#include "ipegroup.h"
35
#include "ipereference.h"
37
36
#include "ipeutils.h"
38
37
#include "ipedoc.h"
40
39
#include "ipepswriter.h"
41
40
#include "ipefontpool.h"
43
44
static const char hexChar[17] = "0123456789abcdef";
45
46
// --------------------------------------------------------------------
47
IpePsPainter::IpePsPainter(const IpeStyleSheet *style, IpeStream &stream)
48
: IpePdfPainter(style, stream)
48
PsPainter::PsPainter(const Cascade *style, Stream &stream)
49
: PdfPainter(style, stream)
50
51
// Postscript has only one current color: we use iStroke for that
54
IpePsPainter::~IpePsPainter()
55
PsPainter::~PsPainter()
59
void IpePsPainter::StrokePath()
60
void PsPainter::doNewPath()
62
// nothing (override PDF implementation)
65
void PsPainter::strokePath()
61
67
State &s = iState.back();
62
68
State &sa = iActiveState.back();
63
const IpeRepository *rep = StyleSheet()->Repository();
64
if (s.iDashStyle && !s.iDashStyle.IsVoid()
65
&& s.iDashStyle != sa.iDashStyle) {
69
if (s.iDashStyle != sa.iDashStyle) {
66
70
sa.iDashStyle = s.iDashStyle;
67
if (s.iDashStyle.IsSolid())
71
if (s.iDashStyle.empty())
68
72
iStream << "[] 0 d ";
70
iStream << rep->String(s.iDashStyle) << " d ";
72
if (s.iLineWidth && s.iLineWidth != sa.iLineWidth) {
73
sa.iLineWidth = s.iLineWidth;
74
iStream << rep->String(s.iLineWidth) << " w ";
76
IpeAttribute cap = s.iLineCap;
78
cap = StyleSheet()->LineCap();
79
if (cap != sa.iLineCap) {
81
iStream << int(cap.Index()) << " J\n";
83
IpeAttribute join = s.iLineJoin;
85
join = StyleSheet()->LineJoin();
86
if (join != sa.iLineJoin) {
88
iStream << int(join.Index()) << " j\n";
74
iStream << s.iDashStyle << " d ";
76
if (s.iPen != sa.iPen) {
78
iStream << s.iPen << " w ";
80
if (s.iLineCap != sa.iLineCap) {
81
sa.iLineCap = s.iLineCap;
82
iStream << s.iLineCap << " J\n";
84
if (s.iLineJoin != sa.iLineJoin) {
85
sa.iLineJoin = s.iLineJoin;
86
iStream << s.iLineJoin << " j\n";
90
88
if (s.iStroke != sa.iStroke) {
91
89
sa.iStroke = s.iStroke;
92
DrawColor(iStream, s.iStroke, "g", "rg");
90
drawColor(iStream, s.iStroke, "g", "rg");
97
void IpePsPainter::FillPath(bool eoFill, bool preservePath)
95
void PsPainter::fillPath(bool eoFill, bool preservePath)
99
State &s = iState.back();
100
State &sa = iActiveState.back();
101
if (s.iFill != sa.iStroke) {
102
sa.iStroke = s.iFill;
103
DrawColor(iStream, s.iFill, "g", "rg");
97
if (tiling().isNormal()) {
98
State &s = iState.back();
99
State &sa = iActiveState.back();
100
if (s.iFill != sa.iStroke) {
101
sa.iStroke = s.iFill;
102
drawColor(iStream, s.iFill, "g", "rg");
105
State &s = iState.back();
106
if (s.iFill.isGray())
107
iStream << s.iFill.iRed << " Pat" << s.iTiling.index() << " patg\n";
109
iStream << s.iFill << " Pat" << s.iTiling.index() << " patrg\n";
110
// set an impossible color
111
iActiveState.back().iStroke.iRed = Fixed(2);
105
113
if (preservePath)
109
117
iStream << " Q ";
112
void IpePsPainter::DoDrawPath()
120
void PsPainter::doDrawPath(TPathMode mode)
114
bool noStroke = Stroke().IsNull() || DashStyle().IsVoid();
115
bool noFill = Fill().IsNullOrVoid();
116
IpeAttribute w = WindRule();
118
w = StyleSheet()->WindRule();
119
bool eoFill = !w.Index();
120
if (noStroke && noFill) {
121
iStream << "np"; // flush path
122
} else if (noStroke) {
123
FillPath(eoFill, false);
127
FillPath(eoFill, true);
122
bool eofill = (fillRule() == EEvenOddRule);
124
// iStream << "np"; // flush path
125
if (mode == EFilledOnly)
126
fillPath(eofill, false);
127
else if (mode == EStrokedOnly)
130
fillPath(eofill, true);
133
void IpePsPainter::DoDrawBitmap(IpeBitmap bitmap)
135
switch (bitmap.ColorSpace()) {
136
case IpeBitmap::EDeviceGray:
136
void PsPainter::doAddClipPath()
138
iStream << "eoclip np\n";
141
void PsPainter::doDrawBitmap(Bitmap bitmap)
143
switch (bitmap.colorSpace()) {
144
case Bitmap::EDeviceGray:
137
145
iStream << "/DeviceGray setcolorspace ";
139
case IpeBitmap::EDeviceRGB:
147
case Bitmap::EDeviceRGB:
140
148
iStream << "/DeviceRGB setcolorspace ";
142
case IpeBitmap::EDeviceCMYK:
150
case Bitmap::EDeviceCMYK:
143
151
iStream << "/DeviceCMYK setcolorspace ";
146
iStream << Matrix() << " cm\n";
154
iStream << matrix() << " cm\n";
147
155
iStream << "<< /ImageType 1\n";
148
iStream << " /Width " << bitmap.Width() << "\n";
149
iStream << " /Height " << bitmap.Height() << "\n";
150
iStream << " /BitsPerComponent " << bitmap.BitsPerComponent() << "\n";
156
iStream << " /Width " << bitmap.width() << "\n";
157
iStream << " /Height " << bitmap.height() << "\n";
158
iStream << " /BitsPerComponent " << bitmap.bitsPerComponent() << "\n";
151
159
iStream << " /Decode [ ";
152
for (int i = 0; i < bitmap.Components(); ++i)
160
for (int i = 0; i < bitmap.components(); ++i)
153
161
iStream << "0 1 ";
154
162
iStream << "]\n";
155
iStream << " /ImageMatrix [ " << bitmap.Width() << " 0 0 "
156
<< -bitmap.Height() << " 0 " << bitmap.Height() << " ]\n";
163
iStream << " /ImageMatrix [ " << bitmap.width() << " 0 0 "
164
<< -bitmap.height() << " 0 " << bitmap.height() << " ]\n";
157
165
iStream << " /DataSource currentfile /ASCII85Decode filter";
158
if (bitmap.Filter() == IpeBitmap::EFlateDecode)
166
if (bitmap.filter() == Bitmap::EFlateDecode)
159
167
iStream << " /FlateDecode filter\n";
160
else if (bitmap.Filter() == IpeBitmap::EDCTDecode)
168
else if (bitmap.filter() == Bitmap::EDCTDecode)
161
169
iStream << " /DCTDecode filter\n";
164
172
iStream << ">>\n";
165
iStream << "%%BeginIpeImage: " << iImageNumber << " "
166
<< bitmap.Size() << "\n";
167
bitmap.SetObjNum(iImageNumber++);
173
iStream << "%%BeginIpeImage: " << iImageNumber
174
<< " " << bitmap.size() << "\n";
175
bitmap.setObjNum(iImageNumber++);
168
176
iStream << "image\n";
169
const char *p = bitmap.Data();
170
const char *p1 = p + bitmap.Size();
171
IpeA85Stream a85(iStream);
177
const char *p = bitmap.data();
178
const char *p1 = p + bitmap.size();
179
A85Stream a85(iStream);
175
183
iStream << "%%EndIpeImage\n";
178
186
// --------------------------------------------------------------------
180
/*! \class IpePsWriter
188
/*! \class ipe::PsWriter
181
189
\brief Create Postscript file.
183
191
This class is responsible for the creation of a Postscript file from the
184
Ipe data. You have to create an IpePsWriter first, providing a stream
192
data. You have to create an PsWriter first, providing a stream
185
193
that has been opened for (binary) writing and is empty.
189
197
//! Create Postscript writer operating on this (open and empty) file.
190
IpePsWriter::IpePsWriter(IpeTellStream &stream, const IpeDocument *doc,
198
PsWriter::PsWriter(TellStream &stream, const Document *doc, bool noColor)
192
199
: iStream(stream), iDoc(doc), iNoColor(noColor)
199
206
//! Create the document header and prolog (the resources).
200
207
/*! Embeds no fonts if \c pool is 0.
201
208
Returns false if a TrueType font is present. */
202
bool IpePsWriter::CreateHeader(IpeString creator, int pno, int view)
209
bool PsWriter::createHeader(int pno, int view)
204
const IpeFontPool *pool = iDoc->FontPool();
205
const IpeDocument::SProperties &props = iDoc->Properties();
211
const FontPool *pool = iDoc->fontPool();
212
const Document::SProperties &props = iDoc->properties();
207
214
// Check needed and supplied fonts, and reject Truetype fonts
208
IpeString needed = "";
209
IpeString neededSep = "%%DocumentNeededResources: font ";
210
IpeString supplied = "";
211
IpeString suppliedSep = "%%DocumentSuppliedResources: font ";
216
String neededSep = "%%DocumentNeededResources: font ";
217
String supplied = "";
218
String suppliedSep = "%%DocumentSuppliedResources: font ";
213
for (IpeFontPool::const_iterator font = pool->begin();
220
for (FontPool::const_iterator font = pool->begin();
214
221
font != pool->end(); ++font) {
215
if (font->iType == IpeFont::ETrueType)
222
if (font->iType == Font::ETrueType)
217
224
if (font->iStandardFont) {
218
225
needed += neededSep;
231
238
iStream << "%!PS-Adobe-3.0 EPSF-3.0\n";
233
240
iStream << "%%Creator: Ipelib " << IPELIB_VERSION;
234
if (!creator.empty())
235
iStream << " (" << creator << ")";
241
if (!props.iCreator.empty())
242
iStream << " (" << props.iCreator << ")";
237
244
// CreationDate is informational, no fixed format
238
245
iStream << "%%CreationDate: " << props.iModified << "\n";
239
// Should use level 1 if no images present?
246
// Should use level 1 if no images or patterns present?
240
247
iStream << "%%LanguageLevel: 2\n";
242
const IpePage *page = iDoc->page(pno);
243
std::vector<bool> layers;
244
page->MakeLayerTable(layers, view, false);
245
IpeBBoxPainter painter(iDoc->StyleSheet());
246
for (IpePage::const_iterator it = page->begin(); it != page->end(); ++it) {
247
if (layers[it->Layer()])
248
it->Object()->Draw(painter);
250
IpeRect r = painter.BBox();
249
const Page *page = iDoc->page(pno);
251
int viewBBoxLayer = page->findLayer("VIEWBBOX");
253
if (viewBBoxLayer >= 0 && page->visible(view, viewBBoxLayer))
254
r = page->viewBBox(iDoc->cascade(), view);
256
r = page->pageBBox(iDoc->cascade());
251
258
iStream << "%%BoundingBox: " // (x0, y0) (x1, y1)
252
<< int(r.Min().iX) << " " << int(r.Min().iY) << " "
253
<< int(r.Max().iX + 1) << " " << int(r.Max().iY + 1) << "\n";
254
iStream << "%%HiResBoundingBox: " << r.Min() << " " << r.Max() << "\n";
259
<< int(r.bottomLeft().x) << " " << int(r.bottomLeft().y) << " "
260
<< int(r.topRight().x + 1) << " " << int(r.topRight().y + 1)
262
iStream << "%%HiResBoundingBox: "
263
<< r.bottomLeft() << " " << r.topRight() << "\n";
256
265
iStream << needed;
257
266
iStream << supplied;
305
314
iStream << "/TJ { 0 0 moveto { dup type /stringtype eq\n";
306
315
iStream << " { show } { ipeFontSize mul -0.001 mul 0 rmoveto } ifelse\n";
307
316
iStream << "} forall } def\n";
318
// embed tiling patterns
320
iDoc->cascade()->allNames(ETiling, ts);
321
for (uint i = 0; i < ts.size(); ++i) {
322
const Tiling *t = iDoc->cascade()->findTiling(ts[i]);
325
<< "/PatternType 1\n" // tiling pattern
326
<< "/PaintType 2\n" // uncolored pattern
327
<< "/TilingType 2\n" // faster
328
<< "/BBox [ 0 0 100 " << t->iStep << " ]\n"
330
<< "/YStep " << t->iStep << "\n"
331
<< "/PaintProc { pop 0 0 100 " << t->iWidth << " re fill} bind\n"
333
<< "[ " << m << " 0 0 ]\n"
335
<< "/Pat" << ts[i].index() << " exch def\n";
338
iStream << "/patg { [/Pattern /DeviceGray ] setcolorspace "
339
<< "setcolor } def\n";
341
iStream << "/patrg { [/Pattern /DeviceRGB ] setcolorspace "
342
<< "setcolor } def\n";
308
345
iStream << "end\n";
309
346
iStream << "%%EndResource\n";
351
IpePsWriter::~IpePsWriter()
388
PsWriter::~PsWriter()
356
393
//! Write all fonts to the Postscript file.
357
void IpePsWriter::EmbedFont(const IpeFont &font)
394
void PsWriter::embedFont(const Font &font)
359
396
// write unencrypted front matter
360
iStream.PutRaw(font.iStreamData.data(), font.iLength1);
397
iStream.putRaw(font.iStreamData.data(), font.iLength1);
361
398
// write encrypted section
362
399
const char *p = font.iStreamData.data() + font.iLength1;
363
400
const char *p1 = font.iStreamData.data() + font.iLength1 + font.iLength2;
366
iStream.PutChar(hexChar[(*p >> 4) & 0x0f]);
367
iStream.PutChar(hexChar[*p & 0x0f]);
403
iStream.putChar(hexChar[(*p >> 4) & 0x0f]);
404
iStream.putChar(hexChar[*p & 0x0f]);
369
406
if (++i % 32 == 0)
387
424
// --------------------------------------------------------------------
389
//! Create contents and page stream for this page view.
390
void IpePsWriter::CreatePageView(int pno, int vno)
426
//! Create contents and page stream for this view.
427
/*! Background is not shown. */
428
void PsWriter::createPageView(int pno, int vno)
392
const IpePage *page = iDoc->page(pno);
393
// Create page stream
394
IpePsPainter painter(iDoc->StyleSheet(), iStream);
395
std::vector<bool> layers;
396
page->MakeLayerTable(layers, vno, false);
397
for (IpePage::const_iterator it = page->begin(); it != page->end(); ++it) {
398
if (layers[it->Layer()])
399
it->Object()->Draw(painter);
430
const Page *page = iDoc->page(pno);
431
PsPainter painter(iDoc->cascade(), iStream);
432
for (int i = 0; i < page->count(); ++i) {
433
if (page->objectVisible(vno, i))
434
page->object(i)->draw(painter);
401
436
iStream << "showpage\n";
404
//! Create the trailer of the Postscsript file.
405
void IpePsWriter::CreateTrailer()
439
//! Create the trailer of the Postscript file.
440
void PsWriter::createTrailer()
407
442
iStream << "%%Trailer\n";
408
443
iStream << "end\n"; // to end ipe dictionary
412
447
// --------------------------------------------------------------------
414
class IpePercentStream : public IpeStream {
449
class PercentStream : public Stream {
416
IpePercentStream(IpeStream &stream)
451
PercentStream(Stream &stream)
417
452
: iStream(stream), iNeedPercent(true) { /* nothing */ }
418
virtual void PutChar(char ch);
453
virtual void putChar(char ch);
422
457
bool iNeedPercent;
425
void IpePercentStream::PutChar(char ch)
460
void PercentStream::putChar(char ch)
427
462
if (iNeedPercent)
428
iStream.PutChar('%');
463
iStream.putChar('%');
430
465
iNeedPercent = (ch == '\n');
433
//! Save Ipe information in XML format.
434
void IpePsWriter::CreateXml(IpeString creator, int compressLevel)
468
//! Save information in XML format.
469
void PsWriter::createXml(int compressLevel)
436
IpePercentStream s1(iStream);
471
PercentStream s1(iStream);
437
472
if (compressLevel > 0) {
438
473
iStream << "%%BeginIpeXml: /FlateDecode\n";
439
IpeA85Stream a85(s1);
440
IpeDeflateStream s2(a85, compressLevel);
441
iDoc->SaveAsXml(s2, creator, true);
475
DeflateStream s2(a85, compressLevel);
476
iDoc->saveAsXml(s2, true);
444
479
iStream << "%%BeginIpeXml:\n";
445
iDoc->SaveAsXml(s1, creator, true);
480
iDoc->saveAsXml(s1, true);
448
483
iStream << "%%EndIpeXml\n";
451
486
// --------------------------------------------------------------------