~ubuntu-branches/ubuntu/precise/ipe/precise

« back to all changes in this revision

Viewing changes to src/ipelib/ipepdfwriter.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Steve M. Robbins
  • Date: 2007-01-09 23:14:51 UTC
  • mfrom: (3.1.4 feisty)
  • Revision ID: james.westby@ubuntu.com-20070109231451-3nd095g7ishc108l
Tags: 6.0pre27-3
* debian/gsfonts-fontmap.xml: New.  Fontmap for fonts from gsfonts package.
* debian/rules: Use gsfonts-fontmap.xml instead of tetex-fontmap.xml.
* debian/control: Add texlive-latex-base dependency as alternative to
  tetex-bin (for pdflatex) and replace tetex-extra by gsfonts (for font
  files).  Patch courtesy of Norbert Preining.  Closes: #378537.

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
/*
5
5
 
6
6
    This file is part of the extensible drawing editor Ipe.
7
 
    Copyright (C) 1993-2004  Otfried Cheong
 
7
    Copyright (C) 1993-2005  Otfried Cheong
8
8
 
9
9
    Ipe is free software; you can redistribute it and/or modify it
10
10
    under the terms of the GNU General Public License as published by
43
43
 
44
44
// --------------------------------------------------------------------
45
45
 
46
 
class IpePdfPainter : public IpePainter {
47
 
public:
48
 
  IpePdfPainter(const IpeStyleSheet *style, IpeStream &stream);
49
 
  virtual ~IpePdfPainter() { }
50
 
 
51
 
  virtual void Rect(const IpeRect &re);
52
 
 
53
 
protected:
54
 
  virtual void DoPush();
55
 
  virtual void DoPop();
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,
60
 
                         const IpeVector &v3);
61
 
  virtual void DoClosePath();
62
 
  virtual void DoDrawPath();
63
 
  virtual void DoDrawBitmap(IpeBitmap bitmap);
64
 
  virtual void DoDrawText(const IpeText *text);
65
 
 
66
 
private:
67
 
  void DrawColor(IpeAttribute color, const char *gray, const char *rgb);
68
 
  void DrawAttributes();
69
 
 
70
 
private:
71
 
  IpeStream &iStream;
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;
76
 
};
77
 
 
78
46
IpePdfPainter::IpePdfPainter(const IpeStyleSheet *style, IpeStream &stream)
79
47
  : IpePainter(style), iStream(stream)
80
48
{
82
50
  state.iStroke = IpeAttribute::Black();
83
51
  state.iFill = IpeAttribute::Black();
84
52
  state.iDashStyle = IpeAttribute::Solid();
85
 
  state.iLineCap = IpeAttribute(IpeAttribute::ELineCap, false, 0);
86
 
  state.iLineJoin = IpeAttribute(IpeAttribute::ELineJoin, false, 0);
 
53
  state.iLineCap = style->LineCap();
 
54
  state.iLineJoin = style->LineJoin();
 
55
  iStream << int(state.iLineCap.Index()) << " J "
 
56
          << int(state.iLineJoin.Index()) << " j\n";
87
57
  iActiveState.push_back(state);
88
58
}
89
59
 
134
104
  iStream << "Q\n";
135
105
}
136
106
 
137
 
void IpePdfPainter::DrawColor(IpeAttribute color, const char *gray,
138
 
                              const char *rgb)
 
107
void IpePdfPainter::DrawColor(IpeStream &stream, IpeAttribute color,
 
108
                              const char *gray, const char *rgb)
139
109
{
140
 
  if (color.IsNullOrVoid())
141
 
    return;
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";
146
114
  else
147
 
    iStream << col << " " << rgb << "\n";
 
115
    stream << col << " " << rgb << "\n";
148
116
}
149
117
 
150
118
void IpePdfPainter::DrawAttributes()
152
120
  State &s = iState.back();
153
121
  State &sa = iActiveState.back();
154
122
  const IpeRepository *rep = StyleSheet()->Repository();
155
 
  if (s.iDashStyle && s.iDashStyle != sa.iDashStyle) {
 
123
  if (s.iDashStyle && !s.iDashStyle.IsVoid()
 
124
      && s.iDashStyle != sa.iDashStyle) {
156
125
    sa.iDashStyle = s.iDashStyle;
157
126
    if (s.iDashStyle.IsSolid())
158
127
      iStream << "[] 0 d\n";
163
132
    sa.iLineWidth = s.iLineWidth;
164
133
    iStream << rep->String(s.iLineWidth) << " w\n";
165
134
  }
166
 
  if (s.iLineCap && s.iLineCap != sa.iLineCap) {
167
 
    sa.iLineCap = s.iLineCap;
168
 
    iStream << int(s.iLineCap.Index()) << " J\n";
169
 
  }
170
 
  if (s.iLineJoin && s.iLineJoin != sa.iLineJoin) {
171
 
    sa.iLineJoin = s.iLineJoin;
172
 
    iStream << int(s.iLineJoin.Index()) << " j\n";
173
 
  }
174
 
  if (!s.iStroke.IsNullOrVoid() && s.iStroke != sa.iStroke) {
 
135
  IpeAttribute cap = s.iLineCap;
 
136
  if (!cap)
 
137
    cap = StyleSheet()->LineCap();
 
138
  if (cap != sa.iLineCap) {
 
139
    sa.iLineCap = cap;
 
140
    iStream << int(cap.Index()) << " J\n";
 
141
  }
 
142
  IpeAttribute join = s.iLineJoin;
 
143
  if (!join)
 
144
    join = StyleSheet()->LineJoin();
 
145
  if (join != sa.iLineJoin) {
 
146
    sa.iLineJoin = join;
 
147
    iStream << int(join.Index()) << " j\n";
 
148
  }
 
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");
177
152
  }
178
153
  if (!s.iFill.IsNullOrVoid() && s.iFill != sa.iFill) {
179
154
    sa.iFill = s.iFill;
180
 
    DrawColor(s.iFill, "g", "rg");
181
 
    /*
182
 
    if (s.iFill.IsNullOrVoid())
183
 
      return;
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";
188
 
    */
 
155
    DrawColor(iStream, s.iFill, "g", "rg");
189
156
  }
190
157
}
191
158
 
192
159
void IpePdfPainter::DoDrawPath()
193
160
{
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();
 
164
  if (!w)
 
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)
216
186
{
217
187
  const IpeText::XForm *xf = text->GetXForm();
218
 
  if (xf) {
219
 
    iStream << Matrix() << " cm " ;
220
 
    iStream << IpeMatrix(xf->iStretch.iX, 0, 0, xf->iStretch.iY, 0, 0)
221
 
            << " cm ";
222
 
    iStream.PutRaw(xf->iStream.data(), xf->iStream.size());
223
 
    iStream << "\n";
 
188
  if (!xf)
 
189
    return;
 
190
 
 
191
  IpeAttribute stroke = Stroke();
 
192
  if (stroke.IsNull())
 
193
    stroke = IpeAttribute::Black();
 
194
 
 
195
  iStream << Matrix() << " cm " ;
 
196
  iStream << IpeMatrix(xf->iStretch.iX, 0, 0, xf->iStretch.iY, 0, 0)
 
197
          << " cm ";
 
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'))
 
202
    ++data;
 
203
  // remove whitespace at end
 
204
  while (data <= fin && (*fin == ' ' || *fin == '\n' || *fin == '\r'))
 
205
    --fin;
 
206
  // now output string
 
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");
 
212
      data += 19;
 
213
    } else
 
214
      iStream.PutChar(*data++);
224
215
  }
 
216
  iStream << "\n";
225
217
}
226
218
 
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.
240
231
 
241
232
  Some reserved PDF object numbers:
242
233
 
251
242
  to pages (if such a shading is defined in the document's style
252
243
  sheet. */
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)
256
247
{
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;
 
254
 
 
255
  // mark all bitmaps as not embedded
 
256
  IpeBitmapFinder bm;
 
257
  iDoc->FindBitmaps(bm);
 
258
  int id = -1;
 
259
  for (std::vector<IpeBitmap>::iterator it = bm.iBitmaps.begin();
 
260
       it != bm.iBitmaps.end(); ++it) {
 
261
    it->SetObjNum(id);
 
262
    --id;
 
263
  }
262
264
 
263
265
  iStream << "%PDF-1.3\n";
264
266
 
 
267
  if (iDoc->Properties().iNumberPages) {
 
268
    iPageNumberFont = StartObject();
 
269
    iStream << "<<\n"
 
270
            << "/Type /Font\n"
 
271
            << "/Subtype /Type1\n"
 
272
            << "/BaseFont /Helvetica\n"
 
273
            << ">> endobj\n";
 
274
  }
 
275
 
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)
348
359
{
349
 
  // no fonts embedded?
350
 
  if (!pool)
351
 
    return;
352
 
 
353
 
  // get fonts that require a cmap
354
 
  std::vector<IpeString> cmapFonts;
355
 
  iDoc->StyleSheet()->AllCMaps(cmapFonts);
356
 
 
357
360
  std::map<int, int> fontNumber;
358
361
 
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";
369
 
    }
370
 
    int cmap = -1;
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);
376
 
      IpeString s;
377
 
      IpeStringStream ss(s);
378
 
      ss << "/CIDInit /ProcSet findresource begin\n"
379
 
         << "12 dict begin\n"
380
 
         << "begincmap\n"
381
 
         << "/CIDSystemInfo\n"
382
 
         << "<< /Registry (Adobe)\n"
383
 
         << "/Ordering (UCS)\n"
384
 
         << "/Supplement 0\n"
385
 
         << ">> def\n"
386
 
         << "/CMapName /Adobe.Identity.UCS def\n"
387
 
         << "/CMapType 2 def\n"
388
 
         << "1 begincodespacerange\n"
389
 
         << "<00> <FF>\n"
390
 
         << "endcodespacerange\n"
391
 
         << "1 beginbfrange\n"
392
 
         << "<00> <FF> <" << fpage << "00>\n"
393
 
         << "endbfrange\n"
394
 
         << "endcmap\n"
395
 
         << "CMapName currentdict /CMap defineresource pop\n"
396
 
         << "end\n"
397
 
         << "end\n";
398
 
      cmap = StartObject();
399
 
      iStream << "<<\n";
400
 
      CreateStream(s.data(), s.size(), false);
401
 
    }
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";
407
 
    if (cmap >= 0)
408
 
      iStream << "/ToUnicode " << cmap << " 0 R\n";
 
362
  // fonts embedded?
 
363
  if (pool) {
 
364
 
 
365
    // get fonts that require a cmap
 
366
    std::vector<IpeString> cmapFonts;
 
367
    iDoc->StyleSheet()->AllCMaps(cmapFonts);
 
368
 
 
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";
 
380
      }
 
381
      int cmap = -1;
 
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);
 
387
        IpeString s;
 
388
        IpeStringStream ss(s);
 
389
        ss << "/CIDInit /ProcSet findresource begin\n"
 
390
           << "12 dict begin\n"
 
391
           << "begincmap\n"
 
392
           << "/CIDSystemInfo\n"
 
393
           << "<< /Registry (Adobe)\n"
 
394
           << "/Ordering (UCS)\n"
 
395
           << "/Supplement 0\n"
 
396
           << ">> def\n"
 
397
           << "/CMapName /Adobe.Identity.UCS def\n"
 
398
           << "/CMapType 2 def\n"
 
399
           << "1 begincodespacerange\n"
 
400
           << "<00> <FF>\n"
 
401
           << "endcodespacerange\n"
 
402
           << "1 beginbfrange\n"
 
403
           << "<00> <FF> <" << fpage << "00>\n"
 
404
           << "endbfrange\n"
 
405
           << "endcmap\n"
 
406
           << "CMapName currentdict /CMap defineresource pop\n"
 
407
           << "end\n"
 
408
           << "end\n";
 
409
        cmap = StartObject();
 
410
        iStream << "<<\n";
 
411
        CreateStream(s.data(), s.size(), false);
 
412
      }
 
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";
 
418
      if (cmap >= 0)
 
419
        iStream << "/ToUnicode " << cmap << " 0 R\n";
 
420
      iStream << ">> endobj\n";
 
421
    }
 
422
  }
 
423
 
 
424
  if (pool || iPageNumberFont >= 0) {
 
425
    iResourceNum = StartObject();
 
426
    iStream << "<< ";
 
427
    if (pool) {
 
428
      for (IpeFontPool::const_iterator font = pool->begin();
 
429
           font != pool->end(); ++font) {
 
430
        iStream << "/F" << font->iLatexNumber << " "
 
431
                << fontNumber[font->iLatexNumber] << " 0 R ";
 
432
      }
 
433
    }
 
434
    if (iPageNumberFont >= 0)
 
435
      iStream << "/F" << iPageNumberFont << " "
 
436
              << iPageNumberFont << " 0 R ";
409
437
    iStream << ">> endobj\n";
410
438
  }
411
 
  iResourceNum = StartObject();
412
 
  iStream << "<< ";
413
 
  for (IpeFontPool::const_iterator font = pool->begin();
414
 
       font != pool->end(); ++font) {
415
 
    iStream << "/F" << font->iLatexNumber << " "
416
 
            << fontNumber[font->iLatexNumber] << " 0 R ";
417
 
  }
418
 
  iStream << ">> endobj\n";
419
439
}
420
440
 
421
441
//! Write a stream.
491
511
 
492
512
// --------------------------------------------------------------------
493
513
 
494
 
IpeRect IpePdfWriter::PaintView(IpeStream &stream, const IpePage *page,
495
 
                                int view)
 
514
IpeRect IpePdfWriter::PaintView(IpeStream &stream, int pno, int view)
496
515
{
 
516
  const IpePage *page = (*iDoc)[pno];
497
517
  // XXX pattern
498
518
  // stream << "/Cs1 cs\n";
499
519
  if (iShadingNum >= 0)
502
522
  IpeBBoxPainter bboxPainter(iDoc->StyleSheet());
503
523
  std::vector<bool> layers;
504
524
  page->MakeLayerTable(layers, view, false);
 
525
 
 
526
  // page numbers
 
527
  if (iPageNumberFont >= 0) {
 
528
    IpeString number;
 
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";
 
534
  }
 
535
 
 
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);
 
541
    if (background)
 
542
      background->Draw(painter);
 
543
  }
 
544
 
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);
539
579
  IpeRect bbox;
540
580
  if (iCompressLevel > 0) {
541
581
    IpeDeflateStream dfStream(sstream, iCompressLevel);
542
 
    bbox = PaintView(dfStream, page, view);
 
582
    bbox = PaintView(dfStream, pno, view);
543
583
    dfStream.Close();
544
584
  } else
545
 
    bbox = PaintView(sstream, page, view);
 
585
    bbox = PaintView(sstream, pno, view);
546
586
 
547
587
  int contentsobj = StartObject();
548
588
  iStream << "<<\n";
576
616
    iStream << ">>\n";
577
617
  }
578
618
 
579
 
  // XXX pattern
580
 
  /*
581
 
  iStream << "/Pattern << /P1 2 0 R >>\n";
582
 
  iStream << "/ColorSpace << /Cs1 [/Pattern /DeviceRGB] >>\n";
583
 
  */
584
619
  iStream << "  >>\n";
585
620
  page->View(view).PageDictionary(iStream);
586
621
  iStream << "/MediaBox [" << iDoc->Properties().iMedia << "]\n";
593
628
  iPageObjectNumbers.push_back(pageobj);
594
629
}
595
630
 
596
 
#if 0
597
 
//! Create PDF pages presenting this Ipe page.
598
 
void IpePdfWriter::CreatePage(int page)
599
 
{
600
 
  for (int view = 0; view < (*iDoc)[page]->CountViews(); ++view)
601
 
    CreatePageView(page, view);
602
 
}
603
 
#endif
604
 
 
605
631
//! Create all PDF pages.
606
 
void IpePdfWriter::CreatePages(bool lastView)
 
632
void IpePdfWriter::CreatePages()
607
633
{
608
634
  for (uint page = 0; page < iDoc->size(); ++page) {
609
635
    int nViews = (*iDoc)[page]->CountViews();
610
 
    if (lastView)
611
 
      CreatePageView(page, lastView - 1);
 
636
    if (iLastView)
 
637
      CreatePageView(page, nViews - 1);
612
638
    else
613
639
      for (int view = 0; view < nViews; ++view)
614
640
        CreatePageView(page, view);
647
673
 
648
674
struct Section {
649
675
  int iPage;
 
676
  int iSeqPage;
650
677
  int iObjNum;
651
678
  std::vector<int> iSubPages;
 
679
  std::vector<int> iSubSeqPages;
652
680
};
653
681
 
654
682
//! Create the bookmarks (PDF outline).
656
684
{
657
685
  // first collect all information
658
686
  std::vector<Section> sections;
 
687
  int seqPg = 0;
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()) {
663
692
      Section sec;
664
693
      sec.iPage = pg;
 
694
      sec.iSeqPage = seqPg;
665
695
      sections.push_back(sec);
 
696
      ipeDebug("section on page %d, seq %d", pg, seqPg);
666
697
    }
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);
 
702
    }
 
703
    seqPg += iLastView ? 1 : (*iDoc)[pg]->CountViews();
669
704
  }
670
705
  iBookmarks = -1;
671
706
  if (sections.empty())
690
725
    iStream << "<<\n/Title ";
691
726
    WriteString((*iDoc)[sections[s].iPage]->Section(0));
692
727
    iStream << "\n/Parent " << iBookmarks << " 0 R\n"
693
 
            << "/Dest [ " << iPageObjectNumbers[sections[s].iPage]
 
728
            << "/Dest [ " << iPageObjectNumbers[sections[s].iSeqPage]
694
729
            << " 0 R /XYZ null null null ]\n";
695
730
    if (s > 0)
696
731
      iStream << "/Prev " << sections[s-1].iObjNum << " 0 R\n";
704
739
    // using ids obj + 1 .. obj + count for the subsections
705
740
    for (int ss = 0; ss < count; ++ss) {
706
741
      int pageNo = sections[s].iSubPages[ss];
 
742
      int seqPageNo = sections[s].iSubSeqPages[ss];
707
743
      StartObject(obj + ss + 1);
708
744
      iStream << "<<\n/Title ";
709
745
      WriteString((*iDoc)[pageNo]->Section(1));
710
746
      iStream << "\n/Parent " << obj << " 0 R\n"
711
 
              << "/Dest [ " << iPageObjectNumbers[pageNo]
 
747
              << "/Dest [ " << iPageObjectNumbers[seqPageNo]
712
748
              << " 0 R /XYZ null null null ]\n";
713
749
      if (ss > 0)
714
750
        iStream << "/Prev " << (obj + ss) << " 0 R\n";
748
784
    iStream << "/PageLabels << /Nums [ ";
749
785
    int count = 0;
750
786
    for (int page = 0; page < int(iDoc->size()); ++page) {
751
 
      int nviews = (*iDoc)[page]->CountViews();
 
787
      int nviews = iLastView ? 1 : (*iDoc)[page]->CountViews();
752
788
      if (nviews > 1) {
753
789
        iStream << count << " <</S /D /P (" << (page + 1) << "-)>>";
754
790
      } else { // one view only!