44
44
// --------------------------------------------------------------------
46
class IpePdfPainter : public IpePainter {
48
IpePdfPainter(const IpeStyleSheet *style, IpeStream &stream);
49
virtual ~IpePdfPainter() { }
51
virtual void Rect(const IpeRect &re);
54
virtual void DoPush();
56
virtual void DoNewPath();
57
virtual void DoMoveTo(const IpeVector &v);
58
virtual void DoLineTo(const IpeVector &v);
59
virtual void DoCurveTo(const IpeVector &v1, const IpeVector &v2,
61
virtual void DoClosePath();
62
virtual void DoDrawPath();
63
virtual void DoDrawBitmap(IpeBitmap bitmap);
64
virtual void DoDrawText(const IpeText *text);
67
void DrawColor(IpeAttribute color, const char *gray, const char *rgb);
68
void DrawAttributes();
72
// iActiveState records the attribute settings that have been
73
// recorded in the PDF output, to avoid repeating them
74
// over and over again.
75
StateSeq iActiveState;
78
46
IpePdfPainter::IpePdfPainter(const IpeStyleSheet *style, IpeStream &stream)
79
47
: IpePainter(style), iStream(stream)
134
104
iStream << "Q\n";
137
void IpePdfPainter::DrawColor(IpeAttribute color, const char *gray,
107
void IpePdfPainter::DrawColor(IpeStream &stream, IpeAttribute color,
108
const char *gray, const char *rgb)
140
if (color.IsNullOrVoid())
142
assert(color.IsAbsolute());
110
assert(!color.IsNullOrVoid() && color.IsAbsolute());
143
111
IpeColor col = StyleSheet()->Repository()->ToColor(color);
144
112
if (col.IsGray())
145
iStream << col.iRed << " " << gray << "\n";
113
stream << col.iRed << " " << gray << "\n";
147
iStream << col << " " << rgb << "\n";
115
stream << col << " " << rgb << "\n";
150
118
void IpePdfPainter::DrawAttributes()
163
132
sa.iLineWidth = s.iLineWidth;
164
133
iStream << rep->String(s.iLineWidth) << " w\n";
166
if (s.iLineCap && s.iLineCap != sa.iLineCap) {
167
sa.iLineCap = s.iLineCap;
168
iStream << int(s.iLineCap.Index()) << " J\n";
170
if (s.iLineJoin && s.iLineJoin != sa.iLineJoin) {
171
sa.iLineJoin = s.iLineJoin;
172
iStream << int(s.iLineJoin.Index()) << " j\n";
174
if (!s.iStroke.IsNullOrVoid() && s.iStroke != sa.iStroke) {
135
IpeAttribute cap = s.iLineCap;
137
cap = StyleSheet()->LineCap();
138
if (cap != sa.iLineCap) {
140
iStream << int(cap.Index()) << " J\n";
142
IpeAttribute join = s.iLineJoin;
144
join = StyleSheet()->LineJoin();
145
if (join != sa.iLineJoin) {
147
iStream << int(join.Index()) << " j\n";
149
if (!s.iStroke.IsNull() && s.iStroke != sa.iStroke) {
175
150
sa.iStroke = s.iStroke;
176
DrawColor(s.iStroke, "G", "RG");
151
DrawColor(iStream, s.iStroke, "G", "RG");
178
153
if (!s.iFill.IsNullOrVoid() && s.iFill != sa.iFill) {
179
154
sa.iFill = s.iFill;
180
DrawColor(s.iFill, "g", "rg");
182
if (s.iFill.IsNullOrVoid())
184
assert(s.iFill.IsAbsolute());
185
IpeColor col = StyleSheet()->Repository()->ToColor(s.iFill);
186
iStream << col.iRed << " " << col.iGreen << " "
187
<< col.iBlue << " /P1 scn\n";
155
DrawColor(iStream, s.iFill, "g", "rg");
192
159
void IpePdfPainter::DoDrawPath()
194
bool noStroke = Stroke().IsNullOrVoid();
161
bool noStroke = Stroke().IsNull() || DashStyle().IsVoid();
195
162
bool noFill = Fill().IsNullOrVoid();
196
bool eofill = !WindRule() || !WindRule().Index();
163
IpeAttribute w = WindRule();
165
w = StyleSheet()->WindRule();
166
bool eofill = !w.Index();
197
167
if (noStroke && noFill)
198
168
iStream << "n\n"; // no op path
199
169
else if (noStroke)
215
185
void IpePdfPainter::DoDrawText(const IpeText *text)
217
187
const IpeText::XForm *xf = text->GetXForm();
219
iStream << Matrix() << " cm " ;
220
iStream << IpeMatrix(xf->iStretch.iX, 0, 0, xf->iStretch.iY, 0, 0)
222
iStream.PutRaw(xf->iStream.data(), xf->iStream.size());
191
IpeAttribute stroke = Stroke();
193
stroke = IpeAttribute::Black();
195
iStream << Matrix() << " cm " ;
196
iStream << IpeMatrix(xf->iStretch.iX, 0, 0, xf->iStretch.iY, 0, 0)
198
const char *data = xf->iStream.data();
199
const char *fin = data + xf->iStream.size() - 1;
200
// skip whitespace at beginning
201
while (data <= fin && (*data == ' ' || *data == '\n' || *data == '\r'))
203
// remove whitespace at end
204
while (data <= fin && (*fin == ' ' || *fin == '\n' || *fin == '\r'))
207
// replace "0 0 0 0 k 0 0 0 0 K" by color command
208
while (data <= fin) {
209
if (data + 19 <= fin && !strncmp(data, "0 0 0 0 k 0 0 0 0 K", 19)) {
210
DrawColor(iStream, stroke, "g", "rg");
211
DrawColor(iStream, stroke, "G", "RG");
214
iStream.PutChar(*data++);
227
219
// --------------------------------------------------------------------
232
224
This class is responsible for the creation of a PDF file from the
233
225
Ipe data. You have to create an IpePdfWriter first, providing a file
234
226
that has been opened for (binary) writing and is empty. Then call
235
EmbedFonts() to embed the IpeFontPool, EmbedBitmaps() to embed the
236
bitmaps, and CreatePages() to embed the pages.
237
\c CreateXmlStream embeds a stream with the XML representation of
238
the Ipe document. Finally, call \c CreateTrailer to complete the PDF
239
document, and close the file.
227
EmbedFonts() to embed the IpeFontPool and CreatePages() to embed the
228
pages. \c CreateXmlStream embeds a stream with the XML
229
representation of the Ipe document. Finally, call \c CreateTrailer
230
to complete the PDF document, and close the file.
241
232
Some reserved PDF object numbers:
251
242
to pages (if such a shading is defined in the document's style
253
244
IpePdfWriter::IpePdfWriter(IpeTellStream &stream, const IpeDocument *doc,
254
bool noShading, int compression)
255
: iStream(stream), iDoc(doc)
245
bool noShading, bool lastView, int compression)
246
: iStream(stream), iDoc(doc), iLastView(lastView)
257
248
iCompressLevel = compression;
258
249
iObjNum = 3; // 0 - 2 are reserved
259
250
iXmlStreamNum = -1; // no XML stream yet
260
251
iResourceNum = -1;
261
252
iShadingNum = -1;
253
iPageNumberFont = -1;
255
// mark all bitmaps as not embedded
257
iDoc->FindBitmaps(bm);
259
for (std::vector<IpeBitmap>::iterator it = bm.iBitmaps.begin();
260
it != bm.iBitmaps.end(); ++it) {
263
265
iStream << "%PDF-1.3\n";
267
if (iDoc->Properties().iNumberPages) {
268
iPageNumberFont = StartObject();
271
<< "/Subtype /Type1\n"
272
<< "/BaseFont /Helvetica\n"
265
276
IpeShading s = iDoc->StyleSheet()->FindShading();
266
277
if (!noShading && s.iType) {
267
278
iShadingNum = StartObject();
346
357
Embeds no fonts if \c pool is 0, but must be called nevertheless. */
347
358
void IpePdfWriter::EmbedFonts(const IpeFontPool *pool)
349
// no fonts embedded?
353
// get fonts that require a cmap
354
std::vector<IpeString> cmapFonts;
355
iDoc->StyleSheet()->AllCMaps(cmapFonts);
357
360
std::map<int, int> fontNumber;
359
for (IpeFontPool::const_iterator font = pool->begin();
360
font != pool->end(); ++font) {
361
int fontDescriptor = -1;
362
if (!font->iFontDescriptor.empty()) {
363
int streamId = StartObject();
364
iStream << "<<\n" << font->iStreamDict;
365
CreateStream(font->iStreamData.data(), font->iStreamData.size(), false);
366
fontDescriptor = StartObject();
367
iStream << "<<\n" << font->iFontDescriptor
368
<< streamId << " 0 R\n" << ">> endobj\n";
371
int j = font->iName.size() - 2;
372
if (j > 0 && std::find(cmapFonts.begin(), cmapFonts.end(),
373
font->iName.substr(0, j)) != cmapFonts.end()) {
374
IpeString fpage = font->iName.substr(j);
375
// int page = std::strtol(fpage.CString(), 0, 16);
377
IpeStringStream ss(s);
378
ss << "/CIDInit /ProcSet findresource begin\n"
381
<< "/CIDSystemInfo\n"
382
<< "<< /Registry (Adobe)\n"
383
<< "/Ordering (UCS)\n"
386
<< "/CMapName /Adobe.Identity.UCS def\n"
387
<< "/CMapType 2 def\n"
388
<< "1 begincodespacerange\n"
390
<< "endcodespacerange\n"
391
<< "1 beginbfrange\n"
392
<< "<00> <FF> <" << fpage << "00>\n"
395
<< "CMapName currentdict /CMap defineresource pop\n"
398
cmap = StartObject();
400
CreateStream(s.data(), s.size(), false);
402
int objectNumber = StartObject();
403
fontNumber[font->iLatexNumber] = objectNumber;
404
iStream << "<<\n" << font->iFontDict;
405
if (fontDescriptor >= 0)
406
iStream << "/FontDescriptor " << fontDescriptor << " 0 R\n";
408
iStream << "/ToUnicode " << cmap << " 0 R\n";
365
// get fonts that require a cmap
366
std::vector<IpeString> cmapFonts;
367
iDoc->StyleSheet()->AllCMaps(cmapFonts);
369
for (IpeFontPool::const_iterator font = pool->begin();
370
font != pool->end(); ++font) {
371
int fontDescriptor = -1;
372
if (!font->iFontDescriptor.empty()) {
373
int streamId = StartObject();
374
iStream << "<<\n" << font->iStreamDict;
375
CreateStream(font->iStreamData.data(),
376
font->iStreamData.size(), false);
377
fontDescriptor = StartObject();
378
iStream << "<<\n" << font->iFontDescriptor
379
<< streamId << " 0 R\n" << ">> endobj\n";
382
int j = font->iName.size() - 2;
383
if (j > 0 && std::find(cmapFonts.begin(), cmapFonts.end(),
384
font->iName.substr(0, j)) != cmapFonts.end()) {
385
IpeString fpage = font->iName.substr(j);
386
// int page = std::strtol(fpage.CString(), 0, 16);
388
IpeStringStream ss(s);
389
ss << "/CIDInit /ProcSet findresource begin\n"
392
<< "/CIDSystemInfo\n"
393
<< "<< /Registry (Adobe)\n"
394
<< "/Ordering (UCS)\n"
397
<< "/CMapName /Adobe.Identity.UCS def\n"
398
<< "/CMapType 2 def\n"
399
<< "1 begincodespacerange\n"
401
<< "endcodespacerange\n"
402
<< "1 beginbfrange\n"
403
<< "<00> <FF> <" << fpage << "00>\n"
406
<< "CMapName currentdict /CMap defineresource pop\n"
409
cmap = StartObject();
411
CreateStream(s.data(), s.size(), false);
413
int objectNumber = StartObject();
414
fontNumber[font->iLatexNumber] = objectNumber;
415
iStream << "<<\n" << font->iFontDict;
416
if (fontDescriptor >= 0)
417
iStream << "/FontDescriptor " << fontDescriptor << " 0 R\n";
419
iStream << "/ToUnicode " << cmap << " 0 R\n";
420
iStream << ">> endobj\n";
424
if (pool || iPageNumberFont >= 0) {
425
iResourceNum = StartObject();
428
for (IpeFontPool::const_iterator font = pool->begin();
429
font != pool->end(); ++font) {
430
iStream << "/F" << font->iLatexNumber << " "
431
<< fontNumber[font->iLatexNumber] << " 0 R ";
434
if (iPageNumberFont >= 0)
435
iStream << "/F" << iPageNumberFont << " "
436
<< iPageNumberFont << " 0 R ";
409
437
iStream << ">> endobj\n";
411
iResourceNum = StartObject();
413
for (IpeFontPool::const_iterator font = pool->begin();
414
font != pool->end(); ++font) {
415
iStream << "/F" << font->iLatexNumber << " "
416
<< fontNumber[font->iLatexNumber] << " 0 R ";
418
iStream << ">> endobj\n";
421
441
//! Write a stream.
502
522
IpeBBoxPainter bboxPainter(iDoc->StyleSheet());
503
523
std::vector<bool> layers;
504
524
page->MakeLayerTable(layers, view, false);
527
if (iPageNumberFont >= 0) {
529
stream << "BT /F" << iPageNumberFont << " 10 Tf 10 10 Td\n"
530
<< "[(" << (pno + 1);
531
if (page->CountViews() > 0 && !iLastView)
532
stream << "-" << (view + 1);
533
stream << ")] TJ ET\n";
536
int bgLayer = page->FindLayer("Background");
537
if (bgLayer < 0 || layers[bgLayer]) {
538
IpeRepository *r = const_cast<IpeRepository *>(iDoc->Repository());
539
IpeAttribute bgSym = r->MakeSymbol(IpeAttribute::ETemplate, "Background");
540
const IpeObject *background = iDoc->StyleSheet()->FindTemplate(bgSym);
542
background->Draw(painter);
505
545
for (IpePage::const_iterator it = page->begin(); it != page->end(); ++it) {
506
546
if (layers[it->Layer()]) {
507
547
it->Object()->Draw(painter);
593
628
iPageObjectNumbers.push_back(pageobj);
597
//! Create PDF pages presenting this Ipe page.
598
void IpePdfWriter::CreatePage(int page)
600
for (int view = 0; view < (*iDoc)[page]->CountViews(); ++view)
601
CreatePageView(page, view);
605
631
//! Create all PDF pages.
606
void IpePdfWriter::CreatePages(bool lastView)
632
void IpePdfWriter::CreatePages()
608
634
for (uint page = 0; page < iDoc->size(); ++page) {
609
635
int nViews = (*iDoc)[page]->CountViews();
611
CreatePageView(page, lastView - 1);
637
CreatePageView(page, nViews - 1);
613
639
for (int view = 0; view < nViews; ++view)
614
640
CreatePageView(page, view);
657
685
// first collect all information
658
686
std::vector<Section> sections;
659
688
for (uint pg = 0; pg < iDoc->size(); ++pg) {
660
689
IpeString s = (*iDoc)[pg]->Section(0);
661
690
IpeString ss = (*iDoc)[pg]->Section(1);
662
691
if (!s.empty()) {
694
sec.iSeqPage = seqPg;
665
695
sections.push_back(sec);
696
ipeDebug("section on page %d, seq %d", pg, seqPg);
667
if (!sections.empty() && !ss.empty())
698
if (!sections.empty() && !ss.empty()) {
668
699
sections.back().iSubPages.push_back(pg);
700
sections.back().iSubSeqPages.push_back(seqPg);
701
ipeDebug("subsection on page %d, seq %d", pg, seqPg);
703
seqPg += iLastView ? 1 : (*iDoc)[pg]->CountViews();
671
706
if (sections.empty())