Skip to content

Print Sheets: pass print settings to the internal PDF exporter and support combined export - #3612

Open
hvacengi wants to merge 3 commits into
pyrevitlabs:developfrom
hvacengi:issue-2970-pdf-export-options
Open

Print Sheets: pass print settings to the internal PDF exporter and support combined export#3612
hvacengi wants to merge 3 commits into
pyrevitlabs:developfrom
hvacengi:issue-2970-pdf-export-options

Conversation

@hvacengi

@hvacengi hvacengi commented Sep 4, 2026

Copy link
Copy Markdown

Description

When Print Sheets exports through the built-in "Revit Internal Printer" (Revit 2022+), it builds a fixed PDFExportOptions and never converts the selected page setup's print options into export options. Nothing from the print setup reaches Revit's PDF exporter, so the page size does not shift to the setup's paper size (output renders at the titleblock's own dimensions, losing the white space between the sheet border and the paper edge), margins and placement are not applied, and colors appear even when the setup asks for black lines.

Selecting "Revit Internal Printer" together with Combine into one file also fails outright, with an error reporting that the print setting is incompatible with the printer. The error is a little misleading: the entry is a synthetic item this tool injects into the printer list rather than a real print driver, so the print manager cannot select it at all. The single sheet path already bypasses the print manager for this case, but the combined path did not.

This pull request converts the page setup's parameters into export options, and adds combined export support for the internal exporter using the selected sheet order. PaperSize, PageOrientation, PaperPlacement, OriginOffsetX/OriginOffsetY, ZoomType, Zoom, ColorDepth and RasterQuality are mapped onto their PDFExportOptions counterparts. PDFExportOptions exposes no hidden line views option of its own, so a page setup asking for raster processing is mapped onto AlwaysUseRaster. Each assignment is applied defensively, so a parameter that a given Revit version does not expose is skipped rather than aborting the export.

Resolving the paper size

This is the only part of the mapping that is not mechanical. PaperSize exposes only a Name and carries no dimensions, and that name comes from whichever print driver was active when the page setup was created, so it varies considerably between drivers. Please see the code comments for more info, or I'm happy to share additional information if needed.


Checklist

Before submitting your pull request, ensure the following requirements are met:

  • (see note below) Code follows the PEP 8 style guide.
  • (see note below) Code has been formatted with Black using the command:
    pipenv run black {source_file_or_directory}
  • Changes are tested and verified to work as expected.

Related Issues

If applicable, link the issues resolved by this pull request:


Additional Notes

Tested in Revit 2024 and 2026 with the Revit Internal Printer selected, covering defined page setups, variable size, paper smaller than the sheet, a 50% reduction onto a half size sheet, combined output, and a non-standard paper size.

Paper size matching was also checked against the full paper lists reported by three drivers: Bluebeam PS, an Oce PlotWave 340, and Microsoft Print to PDF. All standard sizes resolve, and non-standard entries such as ANSI_J4, ISO_A5, the BIND_* bind margin variants, Legal and the envelope sizes fall through to the warning rather than being mapped to a near neighbor.

Black was not run on this file. The file is not currently Black formatted: running Black on the unmodified upstream copy rewrites about 935 lines, which would grow this pull request from roughly 250 changed lines to over 1200, with most of that unrelated to the fix. New statements that ran long were wrapped in Black's style by hand, and the remainder of the new code follows the file's existing conventions for quoting and line continuations. I am happy to reformat the file, either here or separately, if maintainers prefer. In that case, I would request that you perform a review of the code as submitted, and after those issues are addressed I can run a pass through Black for continued review.

Five new lines still exceed 79 columns. They sit inside an existing block indented to 52 columns and consist of attribute chains that cannot be split any further, so Black's own output leaves them at 92 to 101 columns as well. Bringing them within 79 would mean extracting the body of that block into a helper method. I'm happy to do that, but since it would lengthen and complicate the PR, I elected not to do so immediately.

While working on this issue, I noticed that the currently selected printer in Revit's print dialog affects the list of valid page sizes presented. That's beyond the scope of this update, and I don't even know that it's actually an issue. I just wanted to include the comment for context.

hvacengi and others added 3 commits September 3, 2026 17:41
When exporting sheets to PDF through the built-in "Revit Internal
Printer" (Revit 2022+), the tool built a fixed PDFExportOptions and
ignored the selected print setting, so choices such as color depth,
raster quality and hidden-line view rendering were never applied to the
output.

Pass the selected print setting's PrintParameters into pdf_opts() and
copy ColorDepth and RasterQuality onto the PDFExportOptions. PDF export
exposes no hidden line views option of its own, so a print setting that
asks for raster processing is mapped onto AlwaysUseRaster instead.

Each value is applied defensively, so a parameter that a given Revit
version does not expose is skipped rather than aborting the export. When
variable paper sizes are in use the per-sheet print settings apply,
otherwise the single selected setting does.

Refs pyrevitlabs#2970

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Selecting "Revit Internal Printer" together with "Combine into one file"
failed with an error that the printer does not support one of the
requested options. The combined path drove the Revit print manager and
called SelectNewPrintDriver on the "Revit Internal Printer" entry, but
that entry is a synthetic list item rather than a real print driver, so
it can never be selected. Single-sheet export already avoids the print
manager for this case; the combined path did not.

Route the combined case through Document.Export with
PDFExportOptions.Combine instead, mirroring the single-sheet internal
export. The sheets are exported in the order they appear (already
adjusted for the reverse-print option), the print setting's parameters
are honored through pdf_opts(), and the result is a single
"Combined Sheet Set.pdf" in the pyRevit print folder.

Refs pyrevitlabs#2970

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The internal PDF export hardcoded PDFExportOptions.PaperFormat to
Default, which means "use sheet size", and never set paper orientation,
placement, origin offsets or zoom. Every export therefore rendered at the
titleblock's own dimensions using default placement, so the print
setting's paper size and the white space between the sheet border and the
paper edge were both lost. Revit's own print output preserves them.

Map the remaining PrintParameters onto their PDFExportOptions
counterparts: PageOrientation, PaperPlacement, OriginOffsetX/Y, ZoomType
and Zoom transfer directly, while PaperSize is resolved to the matching
ExportPaperFormat member.

Resolving the paper size needs care, because PaperSize reports only a
name and that name comes from whichever print driver was active when the
page setup was created. A standard name is matched first, allowing for
the shorthands and spellings drivers use, from "ANSI D" and "A1" to
Microsoft's "Architecture DSheet". Failing that, a measurement pair in
the name is compared against the nominal size of each standard format,
in either unit and either orientation, within a tolerance that absorbs
the rounding drivers apply. The pair is read tolerantly of separators and
decimals, so both Bluebeam's "ARCH_D_(24.00_x_36.00_Inches)" and a
plotter driver's "D+ (24x36 in)" resolve.

Deliberately, a name is never matched on a leading standard alone.
Bluebeam offers both "ARCH_E1_(30.00_x_42.00_Inches)" and
"ARCH_E1_H(15.00_x_21.00_Inches)", and the half-size sheet must not be
exported as a full one. A size with no standard equivalent keeps the
previous sheet-size behavior and is reported once per size, so the user
knows the size was not applied. Sheet size also remains the default when
no print setting is supplied, so a variable paper setting still resolves
per sheet.

Refs pyrevitlabs#2970

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@devloai devloai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary:

  • Maps print setting parameters (PaperSize, PageOrientation, PaperPlacement, OriginOffsetX/Y, ZoomType, Zoom, ColorDepth, RasterQuality) onto PDFExportOptions for the Revit Internal Printer path (2022+).
  • Adds paper size resolution via name matching and dimension-based fallback to standard ExportPaperFormat values.
  • Adds combined PDF export support for the internal exporter using the user's selected sheet order, bypassing the print manager which cannot address the synthetic printer.

Review Summary:

The implementation is well-structured with thorough paper size resolution logic (name-based and dimension-based matching with aliases and tolerance). Each export option assignment is defensively wrapped in try/except for cross-version compatibility. The review identified six issues: empty except Exception: pass blocks that swallow errors without logging (violating the repo's exception handling guidelines), a module-level mutable WARNED_PAPER_SIZES set that persists across Revit sessions, a potential int/float type mismatch on ZoomPercentage, an undocumented coupling between the combined export path and the combine-checkbox UI guard, missing progress feedback for combined exports, and an inconsistent output filename between the two combined-print paths. Knowledge applied: repo coding guidelines on exception handling, module-level state, and IronPython 2.7 compatibility patterns.

Suggestions

  • Run Black on the Print Sheets script.py in a separate follow-up PR to address the ~935 pre-existing formatting deviations. Apply
  • Add unit tests for the paper format resolution helpers (paper_format_name, paper_format_size, paper_dimensions_mm) covering alias, measurement, and non-standard fallback cases. Apply

Comment on lines +258 to +300
if print_params:
try:
opts.ColorDepth = print_params.ColorDepth
except Exception:
pass
try:
opts.RasterQuality = print_params.RasterQuality
except Exception:
pass
# PDF export has no hidden line views option of its own; asking
# for raster processing rasterizes the whole view instead.
try:
opts.AlwaysUseRaster = (
print_params.HiddenLineViews
== DB.HiddenLineViewsType.RasterProcessing
)
except Exception:
pass
# Paper size, placement and zoom decide whether the page follows
# the print setting or collapses onto the sheet's own extents.
try:
opts.PaperFormat = \
PrintUtils.paper_format(print_params.PaperSize)
except Exception:
pass
try:
opts.PaperOrientation = print_params.PageOrientation
except Exception:
pass
try:
opts.PaperPlacement = print_params.PaperPlacement
except Exception:
pass
try:
opts.OriginOffsetX = print_params.OriginOffsetX
opts.OriginOffsetY = print_params.OriginOffsetY
except Exception:
pass
try:
opts.ZoomType = print_params.ZoomType
opts.ZoomPercentage = print_params.Zoom
except Exception:
pass

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The six try: ... except Exception: pass blocks added to pdf_opts silently swallow all exceptions with no logging. While the intent is to skip parameters not exposed by a given Revit version (e.g. AttributeError), catching all Exception means a genuine bug — an invalid value, a type mismatch, or a Revit API error — is also silently ignored. This is especially problematic here because the entire purpose of this PR is to make these parameter mappings work; if one silently fails, there is no diagnostic trail to explain why the output doesn't match the page setup.

Per the repo's exception handling guidelines: "No empty catch/except blocks — at minimum log the exception or use a specific exception type."

Consider logging at debug level so troubleshooting is possible without changing behavior:

try:
    opts.ColorDepth = print_params.ColorDepth
except Exception as ex:
    logger.debug('Could not map ColorDepth: %s', ex)

Or use AttributeError for the "not exposed in this Revit version" case and log other exceptions:

try:
    opts.ColorDepth = print_params.ColorDepth
except AttributeError:
    pass
except Exception as ex:
    logger.debug('Could not map ColorDepth: %s', ex)
actions

Feedback: Rate this comment to help me improve future code reviews:

  • 👍 Good - Helpful and accurate
  • 👎 Poor - Wrong, unclear, or unhelpful
  • Skip if you don't have any strong opinions either way.


# Sizes already reported as unsupported, so that the warning is issued once
# per size rather than once per sheet.
WARNED_PAPER_SIZES = set()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNED_PAPER_SIZES is a module-level mutable set() used as global state to deduplicate warnings. Per the repo's coding guidelines, module-level globals for state should be avoided in pyRevit scripts/modules because they persist across executions in the same Revit session.

For this specific case the practical concern is minor: the set only ever grows and is only used to suppress duplicate warnings, so a stale entry wouldn't cause a functional bug — just a missed warning on a subsequent run. However, if a user changes their paper size between runs, the old size remains in the set forever, so the warning is permanently suppressed for it even if the paper size later becomes relevant again (e.g. after changing print drivers).

Consider clearing this set at the start of each print operation (e.g. WARNED_PAPER_SIZES.clear() in _export_combined_pdf_internal / _print_sheets_in_order entry points), or moving it to instance state on the print window class so it resets naturally when the window is re-opened.

actions

Feedback: Rate this comment to help me improve future code reviews:

  • 👍 Good - Helpful and accurate
  • 👎 Poor - Wrong, unclear, or unhelpful
  • Skip if you don't have any strong opinions either way.

with revit.Transaction('Reload Keynote File', doc=doc):
DB.KeynoteTable.GetKeynoteTable(doc).Reload(None)

optspdf = PrintUtils.pdf_opts(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _export_combined_pdf_internal method creates a new PDFExportOptions with print_params from self.selected_print_setting.print_params, but unlike the single-sheet path (_print_sheets_in_order), it does not handle per_sheet_psettings (variable paper size mode).

When allows_variable_paper is true, each sheet may have its own print_settings with different paper sizes. The single-sheet path (lines 1370-1377 and 1422-1429) correctly branches on per_sheet_psettings to pick per-sheet PrintParameters. But the combined path always uses self.selected_print_setting.print_params for all sheets.

Looking at the call chain: print_sheets_print_combined_sheets_in_order_export_combined_pdf_internal. The combine checkbox is disabled when allows_variable_paper is true (see _update_combine_option at line 1124), so in practice the combined path should only be reached with a single shared print setting.

However, _export_combined_pdf_internal is called directly from _print_combined_sheets_in_order without re-checking allows_variable_paper. If the guard in _update_combine_option is ever bypassed (e.g. the user changes print settings after the combine checkbox state was set, or programmatic use), the combined path would silently apply the wrong paper size to all sheets. Consider adding a defensive comment documenting this dependency:

# Combined export is only reached when allows_variable_paper is False
# (combine_cb is disabled otherwise), so a single print setting covers all sheets.
actions

Feedback: Rate this comment to help me improve future code reviews:

  • 👍 Good - Helpful and accurate
  • 👎 Poor - Wrong, unclear, or unhelpful
  • Skip if you don't have any strong opinions either way.


optspdf = PrintUtils.pdf_opts(
print_params=self.selected_print_setting.print_params)
try:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The _export_combined_pdf_internal method does not show a progress bar, unlike the single-sheet _print_sheets_in_order path which uses forms.ProgressBar. For a large sheet set being combined into one PDF, the UI will appear frozen during the doc.Export call, which could take a significant amount of time.

The single-sheet paths show progress per sheet (e.g. 'Exporting PDFs... {value} of {max_value}'), but the combined export is a single API call that processes all sheets at once, so per-sheet progress isn't possible. However, consider at least showing a status message or non-cancellable progress indicator before the export begins so the user knows work is in progress. The print-manager combined path at least goes through print_mgr.Apply() / SubmitPrint() which shows Revit's built-in progress dialog.

actions

Feedback: Rate this comment to help me improve future code reviews:

  • 👍 Good - Helpful and accurate
  • 👎 Poor - Wrong, unclear, or unhelpful
  • Skip if you don't have any strong opinions either way.

print_params=self.selected_print_setting.print_params)
try:
PrintUtils.export_combined_pdf(
dirPath, sheet_ids, optspdf, doc, "Combined Sheet Set")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The export_combined_pdf method and export_sheet_pdf both use op.splitext(filename)[0] to derive the output name. For the combined path, the hardcoded "Combined Sheet Set" is passed as the filename, producing a PDF named Combined Sheet Set.pdf.

However, the non-internal combined path (via print manager) uses op.join('C:', 'Ordered Sheet Set.pdf') as the print file name (line 1271). The two paths produce differently-named output files: Ordered Sheet Set.pdf vs Combined Sheet Set.pdf. This inconsistency means users switching between the internal printer and a real printer for combined output will get differently-named files.

Consider using a consistent name across both paths.

actions

Feedback: Rate this comment to help me improve future code reviews:

  • 👍 Good - Helpful and accurate
  • 👎 Poor - Wrong, unclear, or unhelpful
  • Skip if you don't have any strong opinions either way.

pass
try:
opts.ZoomType = print_params.ZoomType
opts.ZoomPercentage = print_params.Zoom

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ZoomPercentage is mapped from print_params.Zoom:

opts.ZoomType = print_params.ZoomType
opts.ZoomPercentage = print_params.Zoom

Per the Revit API, PDFExportOptions.ZoomPercentage expects an int. If print_params.Zoom (from PrintParameters) returns a float (e.g. 50.0), assigning it to ZoomPercentage may raise an exception in some Revit versions — which would be silently swallowed by the surrounding except Exception: pass, causing the zoom to be silently dropped without any diagnostic.

Consider explicitly casting to int:

opts.ZoomPercentage = int(print_params.Zoom)
actions

Feedback: Rate this comment to help me improve future code reviews:

  • 👍 Good - Helpful and accurate
  • 👎 Poor - Wrong, unclear, or unhelpful
  • Skip if you don't have any strong opinions either way.

@jmcouffin
jmcouffin requested a balanced review from Copilot September 4, 2026 15:14
@jmcouffin jmcouffin added the Tools Issues related to pyRevit commands [subsystem] label Sep 4, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The behavioral correctness depends on Revit PDFExportOptions/enum members and per-version print-parameter mapping that cannot be verified in this environment and warrants human confirmation against a live Revit host.

Pull request overview

This PR fixes pyRevit's Print Sheets tool so that, when exporting through the built-in "Revit Internal Printer" (Revit 2022+), the selected page setup's print options are actually converted into PDFExportOptions. Previously a fixed options object was used, so the page rendered at the titleblock's own size (issue #2970) and ignored margins, placement, and color settings. It also enables the previously-failing "Combine into one file" case for the internal exporter by bypassing the print manager (which cannot select the synthetic internal-printer entry).

Changes:

  • Added paper-size resolution helpers plus a print_params mapping in pdf_opts to translate PaperSize, orientation, placement, offsets, zoom, color depth, raster quality, and hidden-line raster processing into PDFExportOptions.
  • Added export_combined_pdf and _export_combined_pdf_internal to support combined PDF output for the internal exporter using the user's chosen sheet order.
  • Routed the single-sheet and linked-sheet internal-printer paths through the new print_params-aware pdf_opts.
File summaries
File Description
extensions/pyRevitTools.extension/pyRevit.tab/Drawing Set.panel/Print Sheets.pushbutton/script.py Adds paper-format lookup tables/helpers, maps print setup parameters onto PDFExportOptions, adds combined internal PDF export, and updates internal-printer export call sites to pass the print parameters.
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@hvacengi

hvacengi commented Sep 4, 2026

Copy link
Copy Markdown
Author

Most of the automated comments look pretty straight forward, I should be able to push updates this weekend. The only exception there is the Sheet Name comment, which may need some coordination. My first instinct is to turn that into something the user can configure instead of a hard coded string.

I've never worked with devloai before, But I'll either address it's comments in code or comment on those specifically if needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Tools Issues related to pyRevit commands [subsystem]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Print Sheets - Printing to Sheet Size not Paper Size

3 participants