Template Components

PDF Assets

Embed PDF pages as inline images or append full PDF documents to a REFRACT PDF output. Two modes (EMBED and APPEND) cover different use cases from extracting a single chart to attaching a complete technical manual. PDF assets work in standalone structured content blocks and inside supplementary annex image grids.

REFRACT PDF can include external PDF files in two ways:

ModeWhat it doesOutput
EMBEDExtracts selected pages, rasterizes to JPEG, embeds as inline imagesPixel images (not searchable text)
APPENDMerges a full PDF as native pages at the end of the documentOriginal PDF pages (text stays searchable)

Both modes accept relative asset paths (resolved from your SharePoint or Blob assets library) or full HTTPS URLs.

Standalone image/image_grid/pdf_appendix now render (fixed)

A standalone image, image_grid, or pdf_appendix block placed directly in a structured_content YAML's top-level sections: array (outside any supplementary_Annex) used to fall through to an unhandled-block-type fallback and print literal [Unsupported block type: ...] text instead of drawing anything. This is fixed: renderContentBlock in structuredContentRenderer.ts now has case 'image' and case 'image_grid' branches, both delegating to _renderImageGrid - the same function supplementary_Annex image grids already used, now exported from supplementaryAnnexRenderer.ts and imported by structuredContentRenderer.ts. A standalone image/image_grid draws real pixels, or the same "[Image unavailable]" fallback an annex image already uses on a failed fetch - never a generic placeholder.

pdf_appendix pages append through the pre-existing pdfLibMerge.ts mechanism (collectPdfAppendices/mergePdfPages in renderOrchestrator.ts), which this fix did not change - that mechanism never went through renderContentBlock to begin with, since appended pages are merged onto the output PDF buffer after the main pdfmake render, not drawn in-flow. What changed is the in-flow marker: renderContentBlock's pdf_appendix case now only throws when PDF asset pre-processing did not run for the whole document at all (detected by a missing _prepared marker on the block) - it no longer leaves a [Unsupported block type: pdf_appendix] line in the document body in that case. A single failing pdf_appendix block (bad path, fetch error) is still silently dropped from the document during pre-processing, with no in-flow diagnostic and only a console.warn for an invalid-PDF-file case (nothing at all for a plain fetch failure) - see Failure Handling below for the per-block diagnostics added in a follow-up hardening change.

Designer/playground preview is a separate limitation

The fix above is a server-side change: resolveStructuredContentNodes (which eventually calls renderContentBlock) is invoked directly from the real render handler in apps/dispatcher-function/src/render.ts, and by processor-function too, per resolveStructuredContent.ts's own pipeline comment - a production PDF exercises this same fixed code, not just a live preview. Separately, and unrelated to this fix: the Designer/playground live preview (apps/website/app/playground/playground-client.js, which calls buildDocDefinitionFromSchema directly in the browser) never runs resolveStructuredContentNodes at all, because that step needs server-side asset-fetch credentials the browser doesn't have - so in the Designer, an entire structured_content node, of any block types, still shows only the placeholder text described under Designer Preview.


EMBED Mode

Extracts one or more pages from a PDF file, renders them to JPEG images, and embeds them inline wherever the block appears in your sections.

YAML Block

sections:
  # ── Single page embed
  - type: image
    src: "Documents/certificate-of-compliance.pdf"
    mode: "embed"

  # ── Multi-page embed with all options
  - type: image
    src: "Documents/inspection-report.pdf"
    mode: "embed"
    pageRange: "1-3"
    extractDpi: 300
    extractQuality: 90
    width: 487
    columns: 3
    show_captions: true

Properties

PropertyTypeDefaultRequiredDescription
type"image" or "figure"-YesStandard image block type.
srcstring-YesPath to the PDF. Relative asset path (for example, "Media/report.pdf") or HTTPS URL.
librarystring"assets"NoAsset library name for a relative src, for the mode: "embed" PDF-extraction path - see Failure Handling below for the fetch/classification mechanism.
mode"embed"-YesMust be "embed" to activate PDF extraction. Without it, the .pdf file is ignored.
pageRangestring"1"NoWhich pages to extract. See page range syntax below.
extractDpinumber150NoRender resolution. 72 = screen, 150 = balanced, 300 = print quality.
extractQualitynumber80NoJPEG compression quality (1 - 100). Lower equals smaller file, more artifacts.
widthnumber540NoTarget column width in points.
columnsnumber2NoWhen multiple pages are extracted, arrange them in a grid with this many columns.
show_captionsbooleanfalseNoShow "Page N" captions below each image in multi-page grid.

Page Range Syntax

ValueExtracts
"1"Page 1 only
"5"Page 5 only
"1-5"Pages 1, 2, 3, 4, 5
"1,3,7"Pages 1, 3, and 7
"1-3,5,8-10"Pages 1, 2, 3, 5, 8, 9, 10

"all" does not extract every page. The extraction call site does not pass the PDF's total page count through to the page-range parser, so "all" always falls back to page 1 only, the same as omitting pageRange. Use an explicit range (for example "1-20") if you need more than the first page and know the page count in advance.

How It Works

The extraction pipeline:

PDF file
  → parse and extract page
  → render to PNG pixel buffer
  → compress PNG to JPEG at extractQuality
  → embed as base64 data URI in the PDF
  • Single page result: replaces the original image block with a JPEG data URI.
  • Multiple pages: replaces the block with an image_grid containing one image per page.

DPI and File Size

The rendered pixel width is: (width ÷ 72) × extractDpi

extractDpiPixel width (at 540 pt)
72540 px
1501,125 px
3002,250 px

Standalone Images (not from a PDF)

A plain image or image_grid block, authored directly in a structured_content YAML's sections: array with an ordinary photo/graphic src (not a PDF), also renders - the same fix as EMBED mode above. It reuses the same _renderImageGrid function an annex image grid uses, so sizing, captions, alt text, PDF/UA figure tagging, and the "[Image unavailable]" fallback all behave identically to an annex-nested image.

sections:
  # ── Single standalone image
  - type: image
    src: "Media/Images/site-photo.jpg"
    caption: "Site overview"

  # ── Standalone image grid
  - type: image_grid
    images:
      - src: "Media/Images/before.jpg"
        caption: "Before"
      - src: "Media/Images/after.jpg"
        caption: "After"
    columns: 2
PropertyTypeDefaultDescription
image.srcstring-Required. Library-relative asset path, HTTPS URL, or data: URI.
image.librarystring"assets"Asset library name for a relative src - see Failure Handling below for the fetch/classification mechanism.
image.captionstring-Optional caption below the image.
image.altTextstring-Optional alt text for accessibility.
image.widthnumber487Target column width in pdfmake points, used for downsampling (DEFAULT_STANDALONE_IMAGE_WIDTH_PT in pdfPreprocessor.ts).
image_grid.imagesarray-Required. Each entry accepts src, caption, altText, library (same shape as a single image block, library per cell so one grid can mix libraries).
image_grid.columnsnumber2Images per row.
image_grid.show_captionsbooleantrueShow captions below each image. Only an explicit false turns captions off (block.show_captions !== false in structuredContentRenderer.ts) - this default is true here, unlike the mode: "embed" multi-page grid above, whose synthesized image_grid still defaults show_captions to false.

APPEND Mode

Merges a complete PDF document as native pages appended after the main document. Text, vectors, and fonts are preserved. Nothing is rasterized.

YAML Block

sections:
  # ── Minimal append
  - type: pdf_appendix
    src: "Attachments/warranty-terms.pdf"

  # ── Full options
  - type: pdf_appendix
    src: "Documentation/safety-manual.pdf"
    library: assets
    title: "Appendix B: Safety Procedures"
    pageNumbers: true

Properties

PropertyTypeDefaultRequiredDescription
type"pdf_appendix"-YesBlock type identifier.
srcstring-YesPDF path: relative asset path or HTTPS URL.
library"assets""assets"NoAsset library name - see Failure Handling below for the fetch/classification mechanism.
titlestring-NoWhen set, a title page is generated and inserted before the appended pages.
pageNumbersbooleanfalseNoWhen true, page numbers continue from the main document onto appended pages.

How It Works

The merge pipeline:

PDF file
  → download or read from assets
  → validate PDF structure
  → store buffer and metadata
  → after main PDF is generated: merge pages into output

Failure Handling

This is a follow-up fix to the standalone-rendering fix above: the library field was defined but silently ignored, and a failed fetch gave no detail beyond a server-side console.warn nobody rendering a document would see.

library now works

library now actually selects the asset library for a standalone image block's src, for each cell in an image_grid.images[] array (so one grid can mix images from different libraries), for the mode: "embed" PDF-extraction path, and for pdf_appendix.src - all default to "assets" when omitted, matching the existing documented default. Cited from origin/main:

// pdfPreprocessor.ts, _resolveStandaloneImageSrc (standalone image/image_grid cell):
const library = typeof target.library === 'string' && target.library.trim() !== '' ? target.library : undefined;
const ref: AnnexImageRef = {
  url:     src,
  caption: typeof target.caption === 'string' ? target.caption : undefined,
  library,
};
// pdfAssetHelper.ts, resolvePdfForAppend (pdf_appendix):
// block.library (a required field on PdfAppendixBlock, documented since [before]) called
// with library omitted, silently defaulting to 'assets' regardless.
const library = typeof block.library === 'string' && block.library.trim() !== '' ? block.library : 'assets';

Fetch failures carry real diagnostic detail

A failed fetch now records which library, which path, and the underlying cause, classified as either author (a wrong path, a wrong library name, a blocked URL - deterministic, content-fixable) or environmental (permissions, a transient backend error, a network timeout, no asset reader configured at all):

// annexImageHelper.ts
export type FetchFailureCategory = 'author' | 'environmental';

export function _classifyAssetFailure(library: string, filePath: string, err: unknown): ClassifiedFetchFailure {
  ...
  if (LIBRARY_NOT_CONFIGURED_RE.test(rawMessage)) {
    return {
      category: 'author',
      message:  `Asset library '${library}' is not configured in this environment. Check the 'library' value on this image (path: '${filePath}').`,
    };
  }
  if (statusCode === 404) {
    return {
      category: 'author',
      message:  `Asset not found: '${filePath}' in library '${library}' (HTTP 404). Check the path and library name.`,
    };
  }
  if (statusCode === 401 || statusCode === 403) {
    return {
      category: 'environmental',
      message:  `Access denied fetching '${filePath}' from library '${library}' (HTTP ${statusCode}). This may be a permissions issue in the storage backend, or an incorrect path/library treated as unauthorized.`,
    };
  }
  ...
}

Failure handling differs by caller

CallerAuthor-fault failure (bad path, bad library, blocked URL)Environmental failure (permissions, network, transient)
Standalone image / image_grid (hand-authored)Fails the whole render - renderContentBlock's 'image'/'image_grid' case throwsDegrades to the existing "[Image unavailable]" placeholder, now showing the real cause
pdf_appendix (hand-authored)Fails the whole render - renderContentBlock's 'pdf_appendix' case throwsDegrades to an inline "[Attachment unavailable]" diagnostic note in place of the missing appended pages, instead of silently dropping the block
supplementary_Annex image grid (payload-driven)Always degrades - shows the reason inline, same as environmentalAlways degrades - shows the reason inline

The annex path never throws, regardless of category, because it is payload-driven and can run many rows in one document - failing the whole render over one bad row in a hundred would be worse than showing that one row's diagnostic. A hand-authored block is different: it is written once, by a person, in small numbers, so a content mistake (not an infrastructure problem) is treated as a real authoring error worth stopping the render for.

The annex's own images payload array (see PDF in Supplementary Annex Image Grids below) also gains per-image library: each entry can now be { "src": "...", "altText": "...", "library": "..." } instead of only a bare string, letting one row mix images from different asset libraries - extractImageRefs in supplementaryAnnexRenderer.ts reads the new field.

Cited from origin/main:

// structuredContentRenderer.ts, case 'image':
case 'image': {
  if (block.src && !block.data && block._authorFaultError) {
    throw new Error(`[StructuredContent] image block failed to resolve: ${block._authorFaultError}`);
  }
  return renderImageBlock(block, theme);
}
// structuredContentRenderer.ts, case 'pdf_appendix' (environmental branch):
if (appendix.failureReason) {
  return [{
    stack: [
      { text: '[Attachment unavailable]', ... },
      { text: appendix.failureReason, ... },
    ],
  } as unknown as Content];
}
// supplementaryAnnexRenderer.ts doc comment on AnnexImageRef.failureCategory:
// A caller that wants an author-fault failure to fail the whole render reads this back --
// see processAnnexImage's own doc comment for the full mechanism and why the annex path
// (this file) does not do that.

Which Mode Handles Fonts Better?

The two modes handle fonts very differently. Choose based on your document's fonts and your use case.

ScenarioAPPENDEMBED
PDF has embedded fonts✅ Perfect - fonts render exactly as original✅ Works - fonts rasterized to pixels at original quality
PDF uses system fonts✅ Works - if system fonts available⚠️ Falls back to default font on mismatch
PDF has corrupted fonts❌ Might render as boxes⚠️ Falls back to default, completes
Font substitution needed❌ Cannot substitute (raw page copy)✅ Handles automatically during extraction
Best use caseTechnical docs, manuals, legal documentsPhoto galleries, extracts, thumbnails

TL;DR

APPEND mode: The PDF's fonts come with it - REFRACT PDF's font library is never consulted. The appended pages render exactly as they were in the source PDF. If fonts are corrupted or missing, the page might have rendering issues.

EMBED mode: Pages are rasterized to JPEG, so fonts become pixels. If a font is missing during extraction, the renderer falls back to a system default - the extraction still succeeds, but might look different from the original.

Choose your mode:

  • Legal document with specific fonts? Use APPEND (preserves exact formatting)
  • Photo gallery with PDFs as thumbnails? Use EMBED (looks like pixels anyway, font fallback is fine)

PDF in Supplementary Annex Image Grids

PDF files can also appear as image references inside a supplementary_annex block's data. When the annex image grid encounters a .pdf URL, it automatically extracts the first page and renders it as a JPEG thumbnail using the same pipeline as EMBED mode.

This allows your payload data to mix regular images and PDF files in the same images array:

{
  "Lines": [
    {
      "No.": "A101",
      "Description": "Foundation Inspection",
      "notes": ["Concrete poured on 2025-11-15"],
      "images": [
        "Media/Images/foundation-photo.jpg",
        "Documents/Reports/soil-test-report.pdf",
        "https://storage.example.com/photos/rebar-layout.png"
      ]
    }
  ]
}

The .pdf file in the images array is detected by file extension. The first page is extracted, rendered to JPEG at the grid's target_dpi and image_quality, and displayed as a thumbnail alongside regular images.

Image Grid YAML with PDF Support

- type: image_grid
  source: "{{item.images}}"
  columns: 2
  target_dpi: 150        # controls PDF render resolution too
  image_quality: 80      # controls PDF JPEG quality too
  show_captions: true
  caption: "Documentation - Item {{item.id}}"

No additional configuration is needed. PDF detection is automatic based on the .pdf file extension.


Complete Copy-Paste Examples

Example 1: Supplementary Annex with Image Grid (includes PDF support)

Template Node (.rpkg)

Add this node to your template's layoutSchema children:

{
  "id": "supplementary-annex",
  "type": "structured_content",
  "props": {
    "assetPath": "Documents/SupplementaryAnnex/supplementary-annex-layout.yaml",
    "library": "assets"
  },
  "pagination": { "breakBefore": "always" }
}

Layout YAML

Upload to: Assets/Documents/SupplementaryAnnex/supplementary-annex-layout.yaml

# ── Supplementary Annex Layout
#
# Per-line-item supporting documentation section.
# Renders a themed header, notes, and photo grid for every qualifying item.
#
# Supports JPEG, PNG, GIF, WebP, SVG images AND .pdf files in the images array.
# PDF files are auto-detected and the first page is extracted as a JPEG thumbnail.

documentType: supplementary-annex
version: "1.0.0"
title: Supporting Documentation

sections:

  - type: supplementary_Annex

    # ── Data source
    # Top-level key in the payload containing the array of line items.
    data_source: "{{Lines}}"

    # ── Field bindings
    notes_field:  notes
    images_field: images
    id_field:     "No."
    name_field:   Description

    # ── Layout options
    showDivider: false
    includeInToc: true
    sectionNumbering: true

    # ── Per-item layout
    item_template:

      # Themed header bar
      - type: sub_header
        text: "Item {{item.id}} - {{item.name}}"
        subtitle: "Document {{Header.No.}} | Date {{Header.Order Date|date}}"
        style: variant_blue_bar

      # One paragraph per note
      - type: repeat_notes
        source: "{{item.notes}}"
        style: paragraph_block

      # Photo grid (2 columns, 150 DPI)
      # PDF files in the images array are auto-detected and rendered as thumbnails.
      - type: image_grid
        source: "{{item.images}}"
        columns: 2
        target_dpi: 150
        image_quality: 80
        show_captions: true
        caption: "Documentation photo - Item {{item.id}}"

Payload

{
  "features": {
    "supplementaryAnnex": true
  },
  "Header": {
    "No.": "SO-2025-001",
    "Order Date": "2025-11-15T00:00:00"
  },
  "Lines": [
    {
      "No.": "A101",
      "Description": "Foundation Repair - Push Piers",
      "notes": [
        "Installed 8 push piers to 22 ft depth.",
        "Load test passed at 35,000 lbs per pier."
      ],
      "images": [
        "Media/Images/push-pier-install.jpg",
        "Documents/Reports/soil-test-report.pdf",
        "Media/Images/load-test-gauge.png"
      ]
    },
    {
      "No.": "B205",
      "Description": "Crawlspace Encapsulation",
      "notes": [
        "20-mil vapor barrier installed across full crawlspace."
      ],
      "images": [
        "Media/Images/crawlspace-before.jpg",
        "Media/Images/crawlspace-after.jpg"
      ]
    }
  ]
}

Example 2: Standalone PDF Embed

Template Node (.rpkg)

{
  "id": "certificate-section",
  "type": "structured_content",
  "props": {
    "assetPath": "Documents/certificate-layout.yaml",
    "library": "assets"
  }
}

Layout YAML

Upload to: Assets/Documents/certificate-layout.yaml

documentType: certificate-embed
version: "1.0.0"
title: Certificates

sections:
  - type: heading
    level: 2
    text: "Certificate of Compliance"

  - type: paragraph
    text: "The following certificate was issued upon completion of the project."

  # Embed first page of the PDF as an inline image
  - type: image
    src: "Documents/Certificates/compliance-cert.pdf"
    mode: "embed"
    pageRange: "1"
    extractDpi: 200
    extractQuality: 85

Example 3: Multi-Page PDF Embed (Grid)

documentType: inspection-gallery
version: "1.0.0"
title: Inspection Report Pages

sections:
  - type: heading
    level: 2
    text: "Inspection Report"

  # Extract pages 1 - 4 and display as a 2×2 grid
  - type: image
    src: "Documents/Reports/full-inspection-report.pdf"
    mode: "embed"
    pageRange: "1-4"
    extractDpi: 150
    extractQuality: 80
    columns: 2
    show_captions: true

Example 4: PDF Appendix

Template Node (.rpkg)

{
  "id": "appendix-safety",
  "type": "structured_content",
  "props": {
    "assetPath": "Documents/appendix-layout.yaml",
    "library": "assets"
  },
  "pagination": { "breakBefore": "always" }
}

Layout YAML

Upload to: Assets/Documents/appendix-layout.yaml

documentType: appendix-collection
version: "1.0.0"
title: Appendices

sections:
  # Append the safety manual as native PDF pages
  - type: pdf_appendix
    src: "Documentation/safety-manual.pdf"
    library: assets
    title: "Appendix A: Safety Procedures"
    pageNumbers: true

  # Append warranty terms (no title page, no page numbers)
  - type: pdf_appendix
    src: "Attachments/warranty-terms.pdf"

Example 5: Mixed Document (Embed + Annex + Append)

A single YAML file can combine all block types:

documentType: complete-project-report
version: "1.0.0"
title: Project Completion Report

sections:
  # ── Standard content
  - type: heading
    level: 1
    text: "Project Summary"

  - type: paragraph
    text: "This report documents the completion of project {{Header.No.}}."

  # ── Inline PDF embed
  - type: heading
    level: 2
    text: "Engineer's Certificate"

  - type: image
    src: "Documents/engineer-certificate.pdf"
    mode: "embed"
    pageRange: "1"
    extractDpi: 200
    extractQuality: 85

  # ── Supplementary annex
  - type: supplementary_Annex
    data_source: "{{Lines}}"
    notes_field: notes
    images_field: images
    id_field: "No."
    name_field: Description
    showDivider: false
    includeInToc: true
    sectionNumbering: true
    item_template:
      - type: sub_header
        text: "Item {{item.id}} - {{item.name}}"
        style: variant_blue_bar
      - type: repeat_notes
        source: "{{item.notes}}"
        style: paragraph_block
      - type: image_grid
        source: "{{item.images}}"
        columns: 2
        target_dpi: 150
        image_quality: 80
        show_captions: true

  # ── Full PDF appendix
  - type: pdf_appendix
    src: "Documentation/safety-manual.pdf"
    title: "Appendix: Safety Manual"
    pageNumbers: true

supplementary_Annex Block Reference

Complete property reference for the supplementary_Annex block type.

Data Binding

PropertyTypeRequiredDescription
data_sourcestringYesVariable reference to the data array, for example, "{{Lines}}". Must resolve to an array.
notes_fieldstring or string[]NoField name(s) containing notes. When omitted, auto-detected by heuristic.
images_fieldstring or string[]NoField name(s) containing image URLs. When omitted, auto-detected by heuristic.
id_fieldstringNoField name for the item identifier (for example, "No."). Used in headers and table of contents.
name_fieldstringNoField name for the item description (for example, "Description"). Used in headers.
children_fieldsstring[]NoField names containing nested child arrays for hierarchical data flattening.

Layout Options

PropertyTypeDefaultDescription
showDividerbooleanfalseRender a thin horizontal rule between consecutive items.
includeInTocbooleanfalseAdd annex items to the document's table of contents.
sectionNumberingbooleanfalseShow sequential section numbers for items in the table of contents.

item_template

An ordered array of layout blocks rendered for each qualifying item.

sub_header

PropertyTypeDefaultDescription
type"sub_header"-Block type.
textstring-Header text. Supports {{item.id}}, {{item.name}}, and payload variables.
subtitlestring-Optional second line. Supports variables and format pipes (`
stylestring-Visual style. variant_blue_bar is currently the only implemented value.

repeat_notes

PropertyTypeDefaultDescription
type"repeat_notes"-Block type.
sourcestring-Variable reference to notes array, for example, "{{item.notes}}".
stylestring-Paragraph style. Options: paragraph_block, compact, etc.

image_grid

PropertyTypeDefaultDescription
type"image_grid"-Block type.
sourcestring-Variable reference to images array, for example, "{{item.images}}".
columnsnumber2Images per row.
target_dpinumber150Downsample DPI. Higher equals sharper, larger files. Also controls PDF page render resolution.
image_qualitynumber80JPEG quality (1 - 100). Also controls PDF page JPEG quality.
show_captionsbooleantrueShow captions below each image.
captionstring-Caption template. Supports {{item.id}}, {{item.name}}, etc.

Image Path Formats

All image and PDF asset paths can use any of these formats:

FormatExampleWhen to use
Relative asset path"Media/Images/photo.jpg"Files uploaded to the assets library. Simplest and most portable.
HTTPS URL"https://storage.example.com/photo.jpg"Pre-authenticated download URLs (SAS tokens, Graph API @microsoft.graph.downloadUrl).
Data URI"data:image/jpeg;base64,/9j/4AA..."Pre-encoded images from the payload (rare, not supported for PDFs).

For SharePoint assets, use the path relative to the assets library root. Use the same format you use to reference YAML files in assetPath.


Feature Flag

The supplementary_Annex block is gated behind a feature flag. When disabled, the entire annex section is silently omitted.

{
  "features": {
    "supplementaryAnnex": true
  }
}

Enable via template settings (default for all renders)

{
  "templateSettings": {
    "supplementaryAnnex": {
      "enabled": true
    }
  }
}

Priority: Payload features.supplementaryAnnex overrides templateSettings.supplementaryAnnex.enabled.


Troubleshooting

SymptomCauseFix
PDF embed shows placeholderRequired dependencies not installedThese are auto-installed as dependencies. Check node_modules is complete.
PDF embed is blank or returns nullmode: "embed" missing from the blockAdd mode: "embed" to the block. Without it, the .pdf src is ignored.
PDF in annex grid shows placeholderFile extension is not .pdfEnsure the URL ends with .pdf (query strings like ?sv=... are fine).
Appendix pages not appearingpdf_appendix fetch/validation failed for that blockCheck the delivered PDF for an inline "[Attachment unavailable]" note - it names the cause. A bad path or library name fails the render outright instead; see Failure Handling.
Large output file sizeHigh DPI or quality on many pagesReduce extractDpi to 150 and extractQuality to 75 - 80.
Annex section missing entirelyFeature flag not setAdd "features": { "supplementaryAnnex": true } to the payload.

On this page