| Index: src/pdf/SkPDFDocument.cpp
 | 
| diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
 | 
| index 66635a98fb43a02a2ae845acc586f3e0c2a6a52a..8e884d5e834f20e5859a57ea0a3963168be92733 100644
 | 
| --- a/src/pdf/SkPDFDocument.cpp
 | 
| +++ b/src/pdf/SkPDFDocument.cpp
 | 
| @@ -93,45 +93,9 @@ int32_t SkPDFObjectSerializer::offset(SkWStream* wStream) {
 | 
|      return SkToS32(offset - fBaseOffset);
 | 
|  }
 | 
|  
 | 
| -static void perform_font_subsetting(
 | 
| -        const SkTArray<sk_sp<const SkPDFDevice>>& pageDevices,
 | 
| -        SkPDFSubstituteMap* substituteMap) {
 | 
| -    SkASSERT(substituteMap);
 | 
| -
 | 
| -    SkPDFGlyphSetMap usage;
 | 
| -    for (const sk_sp<const SkPDFDevice>& pageDevice : pageDevices) {
 | 
| -        usage.merge(pageDevice->getFontGlyphUsage());
 | 
| -    }
 | 
| -    SkPDFGlyphSetMap::F2BIter iterator(usage);
 | 
| -    const SkPDFGlyphSetMap::FontGlyphSetPair* entry = iterator.next();
 | 
| -    while (entry) {
 | 
| -        sk_sp<SkPDFFont> subsetFont(
 | 
| -                entry->fFont->getFontSubset(entry->fGlyphSet));
 | 
| -        if (subsetFont) {
 | 
| -            substituteMap->setSubstitute(entry->fFont, subsetFont.get());
 | 
| -        }
 | 
| -        entry = iterator.next();
 | 
| -    }
 | 
| -}
 | 
| -
 | 
| -static sk_sp<SkPDFDict> create_pdf_page(const SkPDFDevice* pageDevice) {
 | 
| -    auto page = sk_make_sp<SkPDFDict>("Page");
 | 
| -    page->insertObject("Resources", pageDevice->makeResourceDict());
 | 
| -    page->insertObject("MediaBox", pageDevice->copyMediaBox());
 | 
| -    auto annotations = sk_make_sp<SkPDFArray>();
 | 
| -    pageDevice->appendAnnotations(annotations.get());
 | 
| -    if (annotations->size() > 0) {
 | 
| -        page->insertObject("Annots", std::move(annotations));
 | 
| -    }
 | 
| -    auto content = pageDevice->content();
 | 
| -    page->insertObjRef("Contents", sk_make_sp<SkPDFStream>(content.get()));
 | 
| -    return page;
 | 
| -}
 | 
|  
 | 
|  // return root node.
 | 
| -static sk_sp<SkPDFDict> generate_page_tree(
 | 
| -        SkTDArray<SkPDFDict*>& pages,
 | 
| -        SkTDArray<SkPDFDict*>* pageTree) {
 | 
| +static sk_sp<SkPDFDict> generate_page_tree(SkTArray<sk_sp<SkPDFDict>>* pages) {
 | 
|      // PDF wants a tree describing all the pages in the document.  We arbitrary
 | 
|      // choose 8 (kNodeSize) as the number of allowed children.  The internal
 | 
|      // nodes have type "Pages" with an array of children, a parent pointer, and
 | 
| @@ -142,22 +106,18 @@ static sk_sp<SkPDFDict> generate_page_tree(
 | 
|      static const int kNodeSize = 8;
 | 
|  
 | 
|      // curNodes takes a reference to its items, which it passes to pageTree.
 | 
| -    SkTDArray<SkPDFDict*> curNodes;
 | 
| -    curNodes.setReserve(pages.count());
 | 
| -    for (int i = 0; i < pages.count(); i++) {
 | 
| -        SkSafeRef(pages[i]);
 | 
| -        curNodes.push(pages[i]);
 | 
| -    }
 | 
| +    int totalPageCount = pages->count();
 | 
| +    SkTArray<sk_sp<SkPDFDict>> curNodes;
 | 
| +    curNodes.swap(pages);
 | 
|  
 | 
|      // nextRoundNodes passes its references to nodes on to curNodes.
 | 
| -    SkTDArray<SkPDFDict*> nextRoundNodes;
 | 
| -    nextRoundNodes.setReserve((pages.count() + kNodeSize - 1)/kNodeSize);
 | 
| -
 | 
|      int treeCapacity = kNodeSize;
 | 
|      do {
 | 
| +        SkTArray<sk_sp<SkPDFDict>> nextRoundNodes;
 | 
|          for (int i = 0; i < curNodes.count(); ) {
 | 
|              if (i > 0 && i + 1 == curNodes.count()) {
 | 
| -                nextRoundNodes.push(curNodes[i]);
 | 
| +                SkASSERT(curNodes[i]);
 | 
| +                nextRoundNodes.emplace_back(std::move(curNodes[i]));
 | 
|                  break;
 | 
|              }
 | 
|  
 | 
| @@ -167,16 +127,9 @@ static sk_sp<SkPDFDict> generate_page_tree(
 | 
|  
 | 
|              int count = 0;
 | 
|              for (; i < curNodes.count() && count < kNodeSize; i++, count++) {
 | 
| +                SkASSERT(curNodes[i]);
 | 
|                  curNodes[i]->insertObjRef("Parent", newNode);
 | 
| -                kids->appendObjRef(sk_ref_sp(curNodes[i]));
 | 
| -
 | 
| -                // TODO(vandebo): put the objects in strict access order.
 | 
| -                // Probably doesn't matter because they are so small.
 | 
| -                if (curNodes[i] != pages[0]) {
 | 
| -                    pageTree->push(curNodes[i]);  // Transfer reference.
 | 
| -                } else {
 | 
| -                    SkSafeUnref(curNodes[i]);
 | 
| -                }
 | 
| +                kids->appendObjRef(std::move(curNodes[i]));
 | 
|              }
 | 
|  
 | 
|              // treeCapacity is the number of leaf nodes possible for the
 | 
| @@ -188,20 +141,19 @@ static sk_sp<SkPDFDict> generate_page_tree(
 | 
|              // consuming treeCapacity chunks.
 | 
|              int pageCount = treeCapacity;
 | 
|              if (i == curNodes.count()) {
 | 
| -                pageCount = ((pages.count() - 1) % treeCapacity) + 1;
 | 
| +                pageCount = ((totalPageCount - 1) % treeCapacity) + 1;
 | 
|              }
 | 
|              newNode->insertInt("Count", pageCount);
 | 
|              newNode->insertObject("Kids", std::move(kids));
 | 
| -            nextRoundNodes.push(newNode.release());  // Transfer reference.
 | 
| +            nextRoundNodes.emplace_back(std::move(newNode));
 | 
|          }
 | 
| +        SkDEBUGCODE( for (const auto& n : curNodes) { SkASSERT(!n); } );
 | 
|  
 | 
| -        curNodes = nextRoundNodes;
 | 
| -        nextRoundNodes.rewind();
 | 
| +        curNodes.swap(&nextRoundNodes);
 | 
| +        nextRoundNodes.reset();
 | 
|          treeCapacity *= kNodeSize;
 | 
|      } while (curNodes.count() > 1);
 | 
| -
 | 
| -    pageTree->push(curNodes[0]);  // Transfer reference.
 | 
| -    return sk_ref_sp(curNodes[0]);
 | 
| +    return std::move(curNodes[0]);
 | 
|  }
 | 
|  
 | 
|  #if 0
 | 
| @@ -269,16 +221,16 @@ void SkPDFDocument::serialize(const sk_sp<SkPDFObject>& object) {
 | 
|  SkCanvas* SkPDFDocument::onBeginPage(SkScalar width, SkScalar height,
 | 
|                                       const SkRect& trimBox) {
 | 
|      SkASSERT(!fCanvas.get());  // endPage() was called before this.
 | 
| -    if (fPageDevices.empty()) {
 | 
| +    if (fPages.empty()) {
 | 
|          // if this is the first page if the document.
 | 
|          fObjectSerializer.serializeHeader(this->getStream(), fMetadata);
 | 
| +        fDests = sk_make_sp<SkPDFDict>();
 | 
|      }
 | 
|      SkISize pageSize = SkISize::Make(
 | 
|              SkScalarRoundToInt(width), SkScalarRoundToInt(height));
 | 
| -    sk_sp<SkPDFDevice> device(
 | 
| +    fPageDevice.reset(
 | 
|              SkPDFDevice::Create(pageSize, fRasterDpi, this));
 | 
| -    fCanvas = sk_make_sp<SkPDFCanvas>(device);
 | 
| -    fPageDevices.push_back(std::move(device));
 | 
| +    fCanvas = sk_make_sp<SkPDFCanvas>(fPageDevice);
 | 
|      fCanvas->clipRect(trimBox);
 | 
|      fCanvas->translate(trimBox.x(), trimBox.y());
 | 
|      return fCanvas.get();
 | 
| @@ -288,11 +240,28 @@ void SkPDFDocument::onEndPage() {
 | 
|      SkASSERT(fCanvas.get());
 | 
|      fCanvas->flush();
 | 
|      fCanvas.reset(nullptr);
 | 
| +    SkASSERT(fPageDevice);
 | 
| +    fGlyphUsage.merge(fPageDevice->getFontGlyphUsage());
 | 
| +    auto page = sk_make_sp<SkPDFDict>("Page");
 | 
| +    page->insertObject("Resources", fPageDevice->makeResourceDict());
 | 
| +    page->insertObject("MediaBox", fPageDevice->copyMediaBox());
 | 
| +    auto annotations = sk_make_sp<SkPDFArray>();
 | 
| +    fPageDevice->appendAnnotations(annotations.get());
 | 
| +    if (annotations->size() > 0) {
 | 
| +        page->insertObject("Annots", std::move(annotations));
 | 
| +    }
 | 
| +    auto contentData = fPageDevice->content();
 | 
| +    auto contentObject = sk_make_sp<SkPDFStream>(contentData.get());
 | 
| +    this->serialize(contentObject);
 | 
| +    page->insertObjRef("Contents", std::move(contentObject));
 | 
| +    fPageDevice->appendDestinations(fDests.get(), page.get());
 | 
| +    fPages.emplace_back(std::move(page));
 | 
| +    fPageDevice.reset(nullptr);
 | 
|  }
 | 
|  
 | 
|  void SkPDFDocument::onAbort() {
 | 
|      fCanvas.reset(nullptr);
 | 
| -    fPageDevices.reset();
 | 
| +    fPages.reset();
 | 
|      fCanon.reset();
 | 
|      renew(&fObjectSerializer);
 | 
|  }
 | 
| @@ -308,25 +277,13 @@ void SkPDFDocument::setMetadata(const SkDocument::Attribute info[],
 | 
|  
 | 
|  bool SkPDFDocument::onClose(SkWStream* stream) {
 | 
|      SkASSERT(!fCanvas.get());
 | 
| -    if (fPageDevices.empty()) {
 | 
| -        fPageDevices.reset();
 | 
| +    if (fPages.empty()) {
 | 
| +        fPages.reset();
 | 
|          fCanon.reset();
 | 
|          renew(&fObjectSerializer);
 | 
|          return false;
 | 
|      }
 | 
| -    SkTDArray<SkPDFDict*> pages;  // TODO: SkTArray<sk_sp<SkPDFDict>>
 | 
| -    auto dests = sk_make_sp<SkPDFDict>();
 | 
| -
 | 
| -    for (const sk_sp<const SkPDFDevice>& pageDevice : fPageDevices) {
 | 
| -        SkASSERT(pageDevice);
 | 
| -        SkASSERT(fPageDevices[0]->getCanon() == pageDevice->getCanon());
 | 
| -        sk_sp<SkPDFDict> page(create_pdf_page(pageDevice.get()));
 | 
| -        pageDevice->appendDestinations(dests.get(), page.get());
 | 
| -        pages.push(page.release());
 | 
| -    }
 | 
| -
 | 
|      auto docCatalog = sk_make_sp<SkPDFDict>("Catalog");
 | 
| -
 | 
|      sk_sp<SkPDFObject> id, xmp;
 | 
|      #ifdef SK_PDF_GENERATE_PDFA
 | 
|          SkPDFMetadata::UUID uuid = metadata.uuid();
 | 
| @@ -352,24 +309,28 @@ bool SkPDFDocument::onClose(SkWStream* stream) {
 | 
|          docCatalog->insertObject("OutputIntents", std::move(intentArray));
 | 
|      #endif
 | 
|  
 | 
| -    SkTDArray<SkPDFDict*> pageTree;
 | 
| -    docCatalog->insertObjRef("Pages", generate_page_tree(pages, &pageTree));
 | 
| +    docCatalog->insertObjRef("Pages", generate_page_tree(&fPages));
 | 
|  
 | 
| -    if (dests->size() > 0) {
 | 
| -        docCatalog->insertObjRef("Dests", std::move(dests));
 | 
| +    if (fDests->size() > 0) {
 | 
| +        docCatalog->insertObjRef("Dests", std::move(fDests));
 | 
|      }
 | 
|  
 | 
|      // Build font subsetting info before calling addObjectRecursively().
 | 
| -    perform_font_subsetting(fPageDevices, &fObjectSerializer.fSubstituteMap);
 | 
| +    for (const auto& entry : fGlyphUsage) {
 | 
| +        sk_sp<SkPDFFont> subsetFont(
 | 
| +                entry.fFont->getFontSubset(entry.fGlyphSet));
 | 
| +        if (subsetFont) {
 | 
| +            fObjectSerializer.fSubstituteMap.setSubstitute(
 | 
| +                    entry.fFont, subsetFont.get());
 | 
| +        }
 | 
| +    }
 | 
|  
 | 
|      fObjectSerializer.addObjectRecursively(docCatalog);
 | 
|      fObjectSerializer.serializeObjects(this->getStream());
 | 
|      fObjectSerializer.serializeFooter(
 | 
|              this->getStream(), docCatalog, std::move(id));
 | 
| -    pageTree.unrefAll();  // TODO(halcanary): make this unnecesary by
 | 
| -                          // refactoring generate_page_tree().
 | 
| -    pages.unrefAll();
 | 
| -    fPageDevices.reset();
 | 
| +
 | 
| +    SkASSERT(fPages.count() == 0);
 | 
|      fCanon.reset();
 | 
|      renew(&fObjectSerializer);
 | 
|      return true;
 | 
| 
 |