Coverage Report

Created: 2023-09-25 06:41

/src/xpdf-4.04/xpdf/PDFDoc.cc
Line
Count
Source (jump to first uncovered line)
1
//========================================================================
2
//
3
// PDFDoc.cc
4
//
5
// Copyright 1996-2003 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#ifdef USE_GCC_PRAGMAS
12
#pragma implementation
13
#endif
14
15
#include <stdio.h>
16
#include <stdlib.h>
17
#include <stddef.h>
18
#include <string.h>
19
#ifdef _WIN32
20
#  include <windows.h>
21
#endif
22
#include "gmempp.h"
23
#include "GString.h"
24
#include "gfile.h"
25
#include "config.h"
26
#include "GlobalParams.h"
27
#include "Page.h"
28
#include "Catalog.h"
29
#include "Stream.h"
30
#include "XRef.h"
31
#include "Link.h"
32
#include "OutputDev.h"
33
#include "Error.h"
34
#include "ErrorCodes.h"
35
#include "Lexer.h"
36
#include "Parser.h"
37
#include "SecurityHandler.h"
38
#include "UTF8.h"
39
#ifndef DISABLE_OUTLINE
40
#include "Outline.h"
41
#endif
42
#include "OptionalContent.h"
43
#include "PDFDoc.h"
44
45
//------------------------------------------------------------------------
46
47
31.5k
#define headerSearchSize 1024  // read this many bytes at beginning of
48
        //   file to look for '%PDF'
49
50
// Avoid sharing files with child processes on Windows, where sharing
51
// can cause problems.
52
#ifdef _WIN32
53
#  define fopenReadMode "rbN"
54
#  define wfopenReadMode L"rbN"
55
#else
56
0
#  define fopenReadMode "rb"
57
#endif
58
59
//------------------------------------------------------------------------
60
// PDFDoc
61
//------------------------------------------------------------------------
62
63
PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
64
0
         GString *userPassword, PDFCore *coreA) {
65
0
  Object obj;
66
0
  GString *fileName1, *fileName2;
67
#ifdef _WIN32
68
  int n, i;
69
#endif
70
71
0
  init(coreA);
72
73
0
  fileName = fileNameA;
74
#ifdef _WIN32
75
  n = fileName->getLength();
76
  fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t));
77
  for (i = 0; i < n; ++i) {
78
    fileNameU[i] = (wchar_t)(fileName->getChar(i) & 0xff);
79
  }
80
  fileNameU[n] = L'\0';
81
#endif
82
83
0
  fileName1 = fileName;
84
85
  // try to open file
86
0
  fileName2 = NULL;
87
#ifdef VMS
88
  if (!(file = fopen(fileName1->getCString(), fopenReadMode, "ctx=stm"))) {
89
    error(errIO, -1, "Couldn't open file '{0:t}'", fileName1);
90
    errCode = errOpenFile;
91
    return;
92
  }
93
#else
94
0
  if (!(file = fopen(fileName1->getCString(), fopenReadMode))) {
95
0
    fileName2 = fileName->copy();
96
0
    fileName2->lowerCase();
97
0
    if (!(file = fopen(fileName2->getCString(), fopenReadMode))) {
98
0
      fileName2->upperCase();
99
0
      if (!(file = fopen(fileName2->getCString(), fopenReadMode))) {
100
0
  error(errIO, -1, "Couldn't open file '{0:t}'", fileName);
101
0
  delete fileName2;
102
0
  errCode = errOpenFile;
103
0
  return;
104
0
      }
105
0
    }
106
0
    delete fileName2;
107
0
  }
108
0
#endif
109
110
  // create stream
111
0
  obj.initNull();
112
0
  str = new FileStream(file, 0, gFalse, 0, &obj);
113
114
0
  ok = setup(ownerPassword, userPassword);
115
0
}
116
117
#ifdef _WIN32
118
PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
119
         GString *userPassword, PDFCore *coreA) {
120
  OSVERSIONINFO version;
121
  Object obj;
122
  int i;
123
124
  init(coreA);
125
126
  // handle a Windows shortcut
127
  wchar_t wPath[winMaxLongPath + 1];
128
  int n = fileNameLen < winMaxLongPath ? fileNameLen : winMaxLongPath;
129
  memcpy(wPath, fileNameA, n * sizeof(wchar_t));
130
  wPath[n] = L'\0';
131
  readWindowsShortcut(wPath, winMaxLongPath + 1);
132
  int wPathLen = (int)wcslen(wPath);
133
134
  // save both Unicode and 8-bit copies of the file name
135
  fileName = new GString();
136
  fileNameU = (wchar_t *)gmallocn(wPathLen + 1, sizeof(wchar_t));
137
  memcpy(fileNameU, wPath, (wPathLen + 1) * sizeof(wchar_t));
138
  for (i = 0; i < wPathLen; ++i) {
139
    fileName->append((char)fileNameA[i]);
140
  }
141
142
  // try to open file
143
  // NB: _wfopen is only available in NT
144
  version.dwOSVersionInfoSize = sizeof(version);
145
  GetVersionEx(&version);
146
  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
147
    file = _wfopen(fileNameU, wfopenReadMode);
148
  } else {
149
    file = fopen(fileName->getCString(), fopenReadMode);
150
  }
151
  if (!file) {
152
    error(errIO, -1, "Couldn't open file '{0:t}'", fileName);
153
    errCode = errOpenFile;
154
    return;
155
  }
156
157
  // create stream
158
  obj.initNull();
159
  str = new FileStream(file, 0, gFalse, 0, &obj);
160
161
  ok = setup(ownerPassword, userPassword);
162
}
163
#endif
164
165
PDFDoc::PDFDoc(char *fileNameA, GString *ownerPassword,
166
0
         GString *userPassword, PDFCore *coreA) {
167
#ifdef _WIN32
168
  OSVERSIONINFO version;
169
#endif
170
0
  Object obj;
171
#ifdef _WIN32
172
  Unicode u;
173
  int i, j;
174
#endif
175
176
0
  init(coreA);
177
178
0
  fileName = new GString(fileNameA);
179
180
#if defined(_WIN32)
181
  wchar_t wPath[winMaxLongPath + 1];
182
  i = 0;
183
  j = 0;
184
  while (j < winMaxLongPath && getUTF8(fileName, &i, &u)) {
185
    wPath[j++] = (wchar_t)u;
186
  }
187
  wPath[j] = L'\0';
188
  readWindowsShortcut(wPath, winMaxLongPath + 1);
189
  int wPathLen = (int)wcslen(wPath);
190
191
  fileNameU = (wchar_t *)gmallocn(wPathLen + 1, sizeof(wchar_t));
192
  memcpy(fileNameU, wPath, (wPathLen + 1) * sizeof(wchar_t));
193
194
  // NB: _wfopen is only available in NT
195
  version.dwOSVersionInfoSize = sizeof(version);
196
  GetVersionEx(&version);
197
  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
198
    file = _wfopen(fileNameU, wfopenReadMode);
199
  } else {
200
    file = fopen(fileName->getCString(), fopenReadMode);
201
  }
202
#elif defined(VMS)
203
  file = fopen(fileName->getCString(), fopenReadMode, "ctx=stm");
204
#else
205
0
  file = fopen(fileName->getCString(), fopenReadMode);
206
0
#endif
207
208
0
  if (!file) {
209
0
    error(errIO, -1, "Couldn't open file '{0:t}'", fileName);
210
0
    errCode = errOpenFile;
211
0
    return;
212
0
  }
213
214
  // create stream
215
0
  obj.initNull();
216
0
  str = new FileStream(file, 0, gFalse, 0, &obj);
217
218
0
  ok = setup(ownerPassword, userPassword);
219
0
}
220
221
PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
222
36
         GString *userPassword, PDFCore *coreA) {
223
#ifdef _WIN32
224
  int n, i;
225
#endif
226
227
36
  init(coreA);
228
229
36
  if (strA->getFileName()) {
230
0
    fileName = strA->getFileName()->copy();
231
#ifdef _WIN32
232
    n = fileName->getLength();
233
    fileNameU = (wchar_t *)gmallocn(n + 1, sizeof(wchar_t));
234
    for (i = 0; i < n; ++i) {
235
      fileNameU[i] = (wchar_t)(fileName->getChar(i) & 0xff);
236
    }
237
    fileNameU[n] = L'\0';
238
#endif
239
36
  } else {
240
36
    fileName = NULL;
241
#ifdef _WIN32
242
    fileNameU = NULL;
243
#endif
244
36
  }
245
36
  str = strA;
246
36
  ok = setup(ownerPassword, userPassword);
247
36
}
248
249
36
void PDFDoc::init(PDFCore *coreA) {
250
36
  ok = gFalse;
251
36
  errCode = errNone;
252
36
  core = coreA;
253
36
  file = NULL;
254
36
  str = NULL;
255
36
  xref = NULL;
256
36
  catalog = NULL;
257
36
#ifndef DISABLE_OUTLINE
258
36
  outline = NULL;
259
36
#endif
260
36
  optContent = NULL;
261
36
}
262
263
36
GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {
264
265
36
  str->reset();
266
267
  // check header
268
36
  checkHeader();
269
270
  // read the xref and catalog
271
36
  if (!PDFDoc::setup2(ownerPassword, userPassword, gFalse)) {
272
36
    if (errCode == errDamaged || errCode == errBadCatalog) {
273
      // try repairing the xref table
274
36
      error(errSyntaxWarning, -1,
275
36
      "PDF file is damaged - attempting to reconstruct xref table...");
276
36
      if (!PDFDoc::setup2(ownerPassword, userPassword, gTrue)) {
277
36
  return gFalse;
278
36
      }
279
36
    } else {
280
0
      return gFalse;
281
0
    }
282
36
  }
283
284
0
#ifndef DISABLE_OUTLINE
285
  // read outline
286
0
  outline = new Outline(catalog->getOutline(), xref);
287
0
#endif
288
289
  // read the optional content info
290
0
  optContent = new OptionalContent(this);
291
292
293
  // done
294
0
  return gTrue;
295
36
}
296
297
GBool PDFDoc::setup2(GString *ownerPassword, GString *userPassword,
298
72
         GBool repairXRef) {
299
  // read xref table
300
72
  xref = new XRef(str, repairXRef);
301
72
  if (!xref->isOk()) {
302
69
    error(errSyntaxError, -1, "Couldn't read xref table");
303
69
    errCode = xref->getErrorCode();
304
69
    delete xref;
305
69
    xref = NULL;
306
69
    return gFalse;
307
69
  }
308
309
  // check for encryption
310
3
  if (!checkEncryption(ownerPassword, userPassword)) {
311
0
    errCode = errEncrypted;
312
0
    delete xref;
313
0
    xref = NULL;
314
0
    return gFalse;
315
0
  }
316
317
  // read catalog
318
3
  catalog = new Catalog(this);
319
3
  if (!catalog->isOk()) {
320
3
    error(errSyntaxError, -1, "Couldn't read page catalog");
321
3
    errCode = errBadCatalog;
322
3
    delete catalog;
323
3
    catalog = NULL;
324
3
    delete xref;
325
3
    xref = NULL;
326
3
    return gFalse;
327
3
  }
328
329
0
  return gTrue;
330
3
}
331
332
36
PDFDoc::~PDFDoc() {
333
36
  if (optContent) {
334
0
    delete optContent;
335
0
  }
336
36
#ifndef DISABLE_OUTLINE
337
36
  if (outline) {
338
0
    delete outline;
339
0
  }
340
36
#endif
341
36
  if (catalog) {
342
0
    delete catalog;
343
0
  }
344
36
  if (xref) {
345
0
    delete xref;
346
0
  }
347
36
  if (str) {
348
36
    delete str;
349
36
  }
350
36
  if (file) {
351
0
    fclose(file);
352
0
  }
353
36
  if (fileName) {
354
0
    delete fileName;
355
0
  }
356
#ifdef _WIN32
357
  if (fileNameU) {
358
    gfree(fileNameU);
359
  }
360
#endif
361
36
}
362
363
// Check for a PDF header on this stream.  Skip past some garbage
364
// if necessary.
365
36
void PDFDoc::checkHeader() {
366
36
  char hdrBuf[headerSearchSize+1];
367
36
  char *p;
368
36
  int i;
369
370
36
  pdfVersion = 0;
371
36
  memset(hdrBuf, 0, headerSearchSize + 1);
372
36
  str->getBlock(hdrBuf, headerSearchSize);
373
31.4k
  for (i = 0; i < headerSearchSize - 5; ++i) {
374
31.3k
    if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {
375
6
      break;
376
6
    }
377
31.3k
  }
378
36
  if (i >= headerSearchSize - 5) {
379
30
    error(errSyntaxWarning, -1, "May not be a PDF file (continuing anyway)");
380
30
    return;
381
30
  }
382
6
  str->moveStart(i);
383
6
  if (!(p = strtok(&hdrBuf[i+5], " \t\n\r"))) {
384
0
    error(errSyntaxWarning, -1, "May not be a PDF file (continuing anyway)");
385
0
    return;
386
0
  }
387
6
  pdfVersion = atof(p);
388
6
  if (!(hdrBuf[i+5] >= '0' && hdrBuf[i+5] <= '9') ||
389
6
      pdfVersion > supportedPDFVersionNum + 0.0001) {
390
0
    error(errSyntaxWarning, -1,
391
0
    "PDF version {0:s} -- xpdf supports version {1:s} (continuing anyway)",
392
0
    p, supportedPDFVersionStr);
393
0
  }
394
6
}
395
396
3
GBool PDFDoc::checkEncryption(GString *ownerPassword, GString *userPassword) {
397
3
  Object encrypt;
398
3
  GBool encrypted;
399
3
  SecurityHandler *secHdlr;
400
3
  GBool ret;
401
402
3
  xref->getTrailerDict()->dictLookup("Encrypt", &encrypt);
403
3
  if ((encrypted = encrypt.isDict())) {
404
0
    if ((secHdlr = SecurityHandler::make(this, &encrypt))) {
405
0
      if (secHdlr->isUnencrypted()) {
406
  // no encryption
407
0
  ret = gTrue;
408
0
      } else if (secHdlr->checkEncryption(ownerPassword, userPassword)) {
409
  // authorization succeeded
410
0
        xref->setEncryption(secHdlr->getPermissionFlags(),
411
0
          secHdlr->getOwnerPasswordOk(),
412
0
          secHdlr->getFileKey(),
413
0
          secHdlr->getFileKeyLength(),
414
0
          secHdlr->getEncVersion(),
415
0
          secHdlr->getEncAlgorithm());
416
0
  ret = gTrue;
417
0
      } else {
418
  // authorization failed
419
0
  ret = gFalse;
420
0
      }
421
0
      delete secHdlr;
422
0
    } else {
423
      // couldn't find the matching security handler
424
0
      ret = gFalse;
425
0
    }
426
3
  } else {
427
    // document is not encrypted
428
3
    ret = gTrue;
429
3
  }
430
3
  encrypt.free();
431
3
  return ret;
432
3
}
433
434
void PDFDoc::displayPage(OutputDev *out, int page,
435
       double hDPI, double vDPI, int rotate,
436
       GBool useMediaBox, GBool crop, GBool printing,
437
       GBool (*abortCheckCbk)(void *data),
438
0
       void *abortCheckCbkData) {
439
0
  if (globalParams->getPrintCommands()) {
440
0
    printf("***** page %d *****\n", page);
441
0
  }
442
0
  catalog->getPage(page)->display(out, hDPI, vDPI,
443
0
          rotate, useMediaBox, crop, printing,
444
0
          abortCheckCbk, abortCheckCbkData);
445
0
}
446
447
void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
448
        double hDPI, double vDPI, int rotate,
449
        GBool useMediaBox, GBool crop, GBool printing,
450
        GBool (*abortCheckCbk)(void *data),
451
0
        void *abortCheckCbkData) {
452
0
  int page;
453
454
0
  for (page = firstPage; page <= lastPage; ++page) {
455
0
    if (globalParams->getPrintStatusInfo()) {
456
0
      fflush(stderr);
457
0
      printf("[processing page %d]\n", page);
458
0
      fflush(stdout);
459
0
    }
460
0
    displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, printing,
461
0
    abortCheckCbk, abortCheckCbkData);
462
0
    catalog->doneWithPage(page);
463
0
  }
464
0
}
465
466
void PDFDoc::displayPageSlice(OutputDev *out, int page,
467
            double hDPI, double vDPI, int rotate,
468
            GBool useMediaBox, GBool crop, GBool printing,
469
            int sliceX, int sliceY, int sliceW, int sliceH,
470
            GBool (*abortCheckCbk)(void *data),
471
0
            void *abortCheckCbkData) {
472
0
  catalog->getPage(page)->displaySlice(out, hDPI, vDPI,
473
0
               rotate, useMediaBox, crop,
474
0
               sliceX, sliceY, sliceW, sliceH,
475
0
               printing,
476
0
               abortCheckCbk, abortCheckCbkData);
477
0
}
478
479
0
Links *PDFDoc::getLinks(int page) {
480
0
  return catalog->getPage(page)->getLinks();
481
0
}
482
483
0
void PDFDoc::processLinks(OutputDev *out, int page) {
484
0
  catalog->getPage(page)->processLinks(out);
485
0
}
486
487
#ifndef DISABLE_OUTLINE
488
0
int PDFDoc::getOutlineTargetPage(OutlineItem *outlineItem) {
489
0
  LinkAction *action;
490
0
  LinkActionKind kind;
491
0
  LinkDest *dest;
492
0
  GString *namedDest;
493
0
  Ref pageRef;
494
0
  int pg;
495
496
0
  if (outlineItem->pageNum >= 0) {
497
0
    return outlineItem->pageNum;
498
0
  }
499
0
  if (!(action = outlineItem->getAction())) {
500
0
    outlineItem->pageNum = 0;
501
0
    return 0;
502
0
  }
503
0
  kind = action->getKind();
504
0
  if (kind != actionGoTo) {
505
0
    outlineItem->pageNum = 0;
506
0
    return 0;
507
0
  }
508
0
  if ((dest = ((LinkGoTo *)action)->getDest())) {
509
0
    dest = dest->copy();
510
0
  } else if ((namedDest = ((LinkGoTo *)action)->getNamedDest())) {
511
0
    dest = findDest(namedDest);
512
0
  }
513
0
  pg = 0;
514
0
  if (dest) {
515
0
    if (dest->isPageRef()) {
516
0
      pageRef = dest->getPageRef();
517
0
      pg = findPage(pageRef.num, pageRef.gen);
518
0
    } else {
519
0
      pg = dest->getPageNum();
520
0
    }
521
0
    delete dest;
522
0
  }
523
0
  outlineItem->pageNum = pg;
524
0
  return pg;
525
0
}
526
#endif
527
528
0
GBool PDFDoc::isLinearized() {
529
0
  Parser *parser;
530
0
  Object obj1, obj2, obj3, obj4, obj5;
531
0
  GBool lin;
532
533
0
  lin = gFalse;
534
0
  obj1.initNull();
535
0
  parser = new Parser(xref,
536
0
       new Lexer(xref,
537
0
         str->makeSubStream(str->getStart(), gFalse, 0, &obj1)),
538
0
       gTrue);
539
0
  parser->getObj(&obj1);
540
0
  parser->getObj(&obj2);
541
0
  parser->getObj(&obj3);
542
0
  parser->getObj(&obj4);
543
0
  if (obj1.isInt() && obj2.isInt() && obj3.isCmd("obj") &&
544
0
      obj4.isDict()) {
545
0
    obj4.dictLookup("Linearized", &obj5);
546
0
    if (obj5.isNum() && obj5.getNum() > 0) {
547
0
      lin = gTrue;
548
0
    }
549
0
    obj5.free();
550
0
  }
551
0
  obj4.free();
552
0
  obj3.free();
553
0
  obj2.free();
554
0
  obj1.free();
555
0
  delete parser;
556
0
  return lin;
557
0
}
558
559
0
GBool PDFDoc::saveAs(GString *name) {
560
0
  FILE *f;
561
0
  char buf[4096];
562
0
  int n;
563
564
0
  if (!(f = fopen(name->getCString(), "wb"))) {
565
0
    error(errIO, -1, "Couldn't open file '{0:t}'", name);
566
0
    return gFalse;
567
0
  }
568
0
  str->reset();
569
0
  while ((n = str->getBlock(buf, sizeof(buf))) > 0) {
570
0
    fwrite(buf, 1, n, f);
571
0
  }
572
0
  str->close();
573
0
  fclose(f);
574
0
  return gTrue;
575
0
}
576
577
0
GBool PDFDoc::saveEmbeddedFile(int idx, const char *path) {
578
0
  FILE *f;
579
0
  GBool ret;
580
581
0
  if (!(f = fopen(path, "wb"))) {
582
0
    return gFalse;
583
0
  }
584
0
  ret = saveEmbeddedFile2(idx, f);
585
0
  fclose(f);
586
0
  return ret;
587
0
}
588
589
0
GBool PDFDoc::saveEmbeddedFileU(int idx, const char *path) {
590
0
  FILE *f;
591
0
  GBool ret;
592
593
0
  if (!(f = openFile(path, "wb"))) {
594
0
    return gFalse;
595
0
  }
596
0
  ret = saveEmbeddedFile2(idx, f);
597
0
  fclose(f);
598
0
  return ret;
599
0
}
600
601
#ifdef _WIN32
602
GBool PDFDoc::saveEmbeddedFile(int idx, const wchar_t *path, int pathLen) {
603
  FILE *f;
604
  OSVERSIONINFO version;
605
  wchar_t path2w[winMaxLongPath + 1];
606
  char path2c[MAX_PATH + 1];
607
  int i;
608
  GBool ret;
609
610
  // NB: _wfopen is only available in NT
611
  version.dwOSVersionInfoSize = sizeof(version);
612
  GetVersionEx(&version);
613
  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
614
    for (i = 0; i < pathLen && i < winMaxLongPath; ++i) {
615
      path2w[i] = path[i];
616
    }
617
    path2w[i] = 0;
618
    f = _wfopen(path2w, L"wb");
619
  } else {
620
    for (i = 0; i < pathLen && i < MAX_PATH; ++i) {
621
      path2c[i] = (char)path[i];
622
    }
623
    path2c[i] = 0;
624
    f = fopen(path2c, "wb");
625
  }
626
  if (!f) {
627
    return gFalse;
628
  }
629
  ret = saveEmbeddedFile2(idx, f);
630
  fclose(f);
631
  return ret;
632
}
633
#endif
634
635
0
GBool PDFDoc::saveEmbeddedFile2(int idx, FILE *f) {
636
0
  Object strObj;
637
0
  char buf[4096];
638
0
  int n;
639
640
0
  if (!catalog->getEmbeddedFileStreamObj(idx, &strObj)) {
641
0
    return gFalse;
642
0
  }
643
0
  strObj.streamReset();
644
0
  while ((n = strObj.streamGetBlock(buf, sizeof(buf))) > 0) {
645
0
    fwrite(buf, 1, n, f);
646
0
  }
647
0
  strObj.streamClose();
648
0
  strObj.free();
649
0
  return gTrue;
650
0
}
651
652
0
char *PDFDoc::getEmbeddedFileMem(int idx, int *size) {
653
0
  Object strObj;
654
0
  char *buf;
655
0
  int bufSize, sizeInc, n;
656
657
0
  if (!catalog->getEmbeddedFileStreamObj(idx, &strObj)) {
658
0
    return NULL;
659
0
  }
660
0
  strObj.streamReset();
661
0
  bufSize = 0;
662
0
  buf = NULL;
663
0
  do {
664
0
    sizeInc = bufSize ? bufSize : 1024;
665
0
    if (bufSize > INT_MAX - sizeInc) {
666
0
      error(errIO, -1, "embedded file is too large");
667
0
      *size = 0;
668
0
      return NULL;
669
0
    }
670
0
    buf = (char *)grealloc(buf, bufSize + sizeInc);
671
0
    n = strObj.streamGetBlock(buf + bufSize, sizeInc);
672
0
    bufSize += n;
673
0
  } while (n == sizeInc);
674
0
  strObj.streamClose();
675
0
  strObj.free();
676
0
  *size = bufSize;
677
0
  return buf;
678
0
}
679