Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: handle cols_width differently in Quarto environment #603

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 70 additions & 1 deletion great_tables/_spanners.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
from __future__ import annotations

import itertools
import warnings

from typing import TYPE_CHECKING

from typing_extensions import TypeAlias

from ._gt_data import SpannerInfo, Spanners
from ._locations import resolve_cols_c
from ._options import tab_options
from ._render import infer_render_env
from ._tbl_data import SelectExpr
from ._text import BaseText, Text
from ._utils import OrderedSet, _assert_list_is_subset
from typing_extensions import TypeAlias

if TYPE_CHECKING:
from ._gt_data import Boxhead
Expand Down Expand Up @@ -581,6 +586,59 @@
return [{var: var for var in vars}], vars


def _quarto_cols_width(self: GTSelf, cases: dict[str, str]) -> tuple[GTSelf, dict[str, str]]:
"""Configure cols_width on a table rendered in a Quarto environment.

Quarto uses Pandoc internally to handle tables, and Pandoc tables do not support pixel widths.
As a result, when cols_width is used in a Quarto environment, widths need to converted to
percentages.

This is only possible when every column width is specified. If the given specification
isn't complete, GT will instead instruct Quarto to not process the produced table.
"""

# Only process if all columns are specified
curr_boxhead = self._boxhead
column_names = set(curr_boxhead._get_columns())
case_names = set(cases.keys())
if column_names != case_names and not self._options.quarto_disable_processing:
warnings.warn(

Check warning on line 605 in great_tables/_spanners.py

View check run for this annotation

Codecov / codecov/patch

great_tables/_spanners.py#L601-L605

Added lines #L601 - L605 were not covered by tests
"This column width specification is not supported by Quarto "
"unless quarto_disable_processing is set to True.",
category=UserWarning,)
return (self, cases)

Check warning on line 609 in great_tables/_spanners.py

View check run for this annotation

Codecov / codecov/patch

great_tables/_spanners.py#L609

Added line #L609 was not covered by tests

sum_of_pixels = sum([int(width[:-2]) for width in cases.values() if width[-2:] == "px"])

Check warning on line 611 in great_tables/_spanners.py

View check run for this annotation

Codecov / codecov/patch

great_tables/_spanners.py#L611

Added line #L611 was not covered by tests
# If there are no pixel widths, return the original cases
if sum_of_pixels == 0:
return (self, cases)

Check warning on line 614 in great_tables/_spanners.py

View check run for this annotation

Codecov / codecov/patch

great_tables/_spanners.py#L613-L614

Added lines #L613 - L614 were not covered by tests

# Handle mixed pixel and percentage widths
# by converting existing pixel widths to percentages
# This function doesn't validate that the sum of percentages is <= 100%

remaining_sum_of_percentages = sum(

Check warning on line 620 in great_tables/_spanners.py

View check run for this annotation

Codecov / codecov/patch

great_tables/_spanners.py#L620

Added line #L620 was not covered by tests
[int(width[:-1]) for width in cases.values() if width[-1] == "%"]
)
sum_of_percentages = 100 - remaining_sum_of_percentages
total_pixels = sum_of_pixels * (100 / sum_of_percentages)
self = tab_options(self, table_width=f"{total_pixels}px")

Check warning on line 625 in great_tables/_spanners.py

View check run for this annotation

Codecov / codecov/patch

great_tables/_spanners.py#L623-L625

Added lines #L623 - L625 were not covered by tests
# Example: [30px, 50px, 20%]
# sum_of_pixels = 80
# sum_of_percentages = 20
# total_pixels = 100

# now convert pixel widths to percentages
new_cases: dict[str, str] = {}
for col, width in cases.items():
if width[-2:] == "px":
new_cases[col] = f"{(int(width[:-2]) / total_pixels) * 100}%"

Check warning on line 635 in great_tables/_spanners.py

View check run for this annotation

Codecov / codecov/patch

great_tables/_spanners.py#L632-L635

Added lines #L632 - L635 were not covered by tests
else:
new_cases[col] = width

Check warning on line 637 in great_tables/_spanners.py

View check run for this annotation

Codecov / codecov/patch

great_tables/_spanners.py#L637

Added line #L637 was not covered by tests

return (self, new_cases)

Check warning on line 639 in great_tables/_spanners.py

View check run for this annotation

Codecov / codecov/patch

great_tables/_spanners.py#L639

Added line #L639 was not covered by tests


def cols_width(self: GTSelf, cases: dict[str, str] | None = None, **kwargs: str) -> GTSelf:
"""Set the widths of columns.

Expand All @@ -589,6 +647,11 @@
Width assignments are supplied inside of a dictionary where columns are the keys and the
corresponding width is the value.

If great_tables is being used in a Quarto environment, a pixel configuration in cols_width will
be converted to percentages. In this case, great_tables will also automatically
set `tab_options(table_width = sum_of_pixels)`. This happens because Pandoc does not
support pixel widths in its column specifications.

Parameters
----------
cases
Expand Down Expand Up @@ -691,6 +754,12 @@
cases = cases if cases is not None else {}
new_cases = cases | kwargs

# Check if we are rendering in the Quarto environment
# If so, convert pixel widths to percentages
env = infer_render_env()
if env == "quarto":
(self, new_cases) = _quarto_cols_width(self, new_cases)

Check warning on line 761 in great_tables/_spanners.py

View check run for this annotation

Codecov / codecov/patch

great_tables/_spanners.py#L761

Added line #L761 was not covered by tests

# If nothing is provided, return `data` unchanged
if len(new_cases) == 0:
return self
Expand Down
Loading