# Table Formatting: How-To Guide

A practical guide for resolving common table formatting issues in S-Docs templates across DOC, DOC-NEW, and PDF formats.

***

### 1. Reducing Row and Cell Spacing

Applies to: `DOC`, `DOC-NEW`, `PDF`

Problem: Tables display excessive whitespace between rows or within cells.

Fix: Apply specific CSS and HTML attributes to the `<table>`, `<th>`, and `<td>` elements to collapse the default spacing.

**On `<th>` and `<td>` elements:**

Reduces spacing inside individual cells.

HTML

```
<td cellpadding="0" cellspacing="0" style="padding: 0; margin: 0;">
```

**On the `<table>` element:**

Reduces spacing between rows.

HTML

```
<table border-spacing="0" style="padding: 0; margin: 0;">
```

{% hint style="info" %}
If a `width` attribute is set on the table, the renderer may add spacing to fill that width. Try removing or reducing the width value if spacing persists after applying the styles above.
{% endhint %}

***

### 2. Handling Content Overflow & Dynamic Spacing

Problem: Tables don't auto-adjust when large amounts of data are pulled in, causing overflow or inconsistent gaps.

Fix: Use CSS as the primary tool for control. It offers more precision than DOCX but requires manual styling.

* Explicit Control: Use CSS on `<table>`, `<tr>`, `<td>`, and `<th>` elements to strictly define overflow behavior.
* Flatten Components: Avoid nesting components that contain tables inside another table. This often causes unpredictable spacing.
* Refactor Nested Layouts: If a template uses inserted components (e.g., for Plaintiff/Defendant sections), isolate and rework those components to remove table nesting.

***

### 3. Resolving Gaps in Dynamic Tables (LineItemsSOQL)

Problem: A gap appears between columns even when padding and spacing are set to zero.

Fix: This is usually a CSS conflict. Audit your template's inline styles for conflicting width, padding, or border declarations.

Checklist for success:

1. Ensure inner `<table>` elements have `width: 100%` and `border-collapse: collapse`.
2. Confirm `cellpadding="0"` and `cellspacing="0"` are applied to both outer and inner tables.
3. Verify no border/padding on a `<td>` is overriding the inner table layout.

Clean Inner Table Setup Example:

HTML

```
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; width: 99%;">
```

***

### 4. Fixing Overflow in PDF Documents

Problem: PDF tables do not auto-adjust cell sizes, causing text or images to bleed outside the table or page boundaries.

#### Text Overflow

Use the `breakeverynchars` attribute on your field column to force line breaks.

XML

```
<column breakeverynchars="40">YourFieldName__c</column>
```

#### Table Width Overflow & Missing Borders

Convert hardcoded pixel widths to percentage-based widths.

| **❌ Avoid**                     | **✅ Use**                     |
| ------------------------------- | ----------------------------- |
| `<table style="width: 600px;">` | `<table style="width: 99%;">` |

Alternatively, you can also add a 1px right margin to bring the table in 1px from the right with minimal impact for formatting.

{% code overflow="wrap" %}

```
table.table123 {margin-right:1px;}
```

{% endcode %}

***

#### Image Overflow

Locate the image tag and replace hardcoded widths with percentages:

HTML

```
<img src="..." style="width: 100%;" />
```

{% hint style="info" %}
If images are pasted into rich text fields by users, S-Docs cannot resize them automatically. You may need to use an Apex class or validation rules to enforce size limits at the point of upload.
{% endhint %}

***

### 5. Dynamic Table Solutions Summary

#### Use `allprefix` / `allpostfix` Instead of `prefix` / `postfix` for Nullable Fields

When a column has a `render` condition and the field value is null, `prefix` and `postfix` are only injected when the value is not null. This means closing tags like `</td></tr>` get swallowed entirely, breaking the table row structure. The fix is to use `allprefix` / `allpostfix` instead — these always render regardless of whether the field value is null, ensuring the row's HTML is always complete and valid.

{% hint style="info" %}
Attributes like prefix/postfix only show if the column value is not null. Use allprefix/allpostfix to ensure all table code is always inserted.
{% endhint %}

#### Avoid Inline Font-Family Quotes in Dynamic Columns

Fonts like `'Trebuchet MS'` use single quotes in CSS — and when embedded inside S-Docs `prefix` / `postfix` attribute strings (which are themselves single-quoted), this causes silent parsing issues. If border or row problems persist even after other fixes, move the font styling to a CSS class rather than embedding it inline in the column tag. This avoids quote-collision bugs that can be very hard to track down.

#### Transposing Data (Rows as Fields, Columns as Records) Requires a Structural Rethink

The standard `LineItemsSOQL` tag outputs one row per record. When the requirement is the *opposite* — field names as rows and each record as a column — a direct dynamic table won't achieve this out of the box. The solution is to build the static row structure in HTML first (one `<tr>` per field label), then use the dynamic SOQL tag to inject values into the correct column positions. With a known fixed number of records (e.g., exactly 3), this is entirely achievable by pre-defining the column layout.

#### Nested / Grouped Tables with Callable Apex or Related Lists

When the requirement is to group line items by a parent value (e.g., a Service SKU) and render a sub-table of child records beneath each parent, there are two approaches:

* Callable Apex — the most flexible route for complex grouping logic, allowing you to structure data into parent/child collections before it reaches the template
* Related List with a Formula Field — a simpler option that can work around the single-field `GROUP BY` limitation without requiring custom Apex, depending on the data model

Both options were validated as viable paths, with the choice depending on complexity and how far along the implementation is.

#### Dynamic Page-per-Item Layout with Conditional Section Display

For templates where each queried record needs its own page, the approach involves combining a component-based SOQL query with page break styling and careful use of static vs. dynamic sections:

* Elements that should appear only once (e.g., a header with spec number and revision) are placed *outside* the repeating block
* For dynamic item counts (e.g., total pages), a Salesforce formula field counting related records is the most reliable approach, as S-Docs doesn't natively expose a record count variable

***

### 6. Managing Table Page Breaks in S-Docs

This guide covers common issues and solutions for controlling how tables break across pages, specifically when dealing with borders, headers, and Callable Apex.

#### Basic Page Break Prevention

Issue: Prevent page breaks from splitting rows within a specific table.

Solution: Apply `page-break-inside: avoid` directly to the `<table>` tag.

HTML

```
<table style="page-break-inside: avoid">
  </table>
```

{% hint style="info" %}
Caveat: This works best when the table is small enough to fit on a single page. If the table is larger than one page, the entire element may be pushed to the next page, creating large gaps in your document.
{% endhint %}

***

#### Related List Flow & Repeating Headers

Issue: Ensuring related list rows flow naturally to the next page and headers repeat for readability.

Solution:

* Automatic Flow: S-Docs automatically breaks related list tables across pages by default—no special configuration is required.
* Repeating Headers: To repeat headers on every page, you must modify the template using the `sd:repeatheader` attribute.
* Manual Breaks: If you need to force a break for the entire table (rather than just repeating headers), apply the property to the `<table>` or `<tbody>` tags.

***

#### Complex Tables with Borders (Callable Apex)

Issue: Maintaining clean borders when a table spans multiple pages (e.g., ensuring a bottom border on Page 1 and a top border on Page 2).

#### Tested Solutions & Results

| **Approach**               | **Result**                                                                                                            |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Avoid on `<table>`         | Pushes entire table; loses bottom border on Page 1.                                                                   |
| Avoid on `<tbody>`         | Content still pushes rather than breaking cleanly.                                                                    |
| Avoid on `<tr>` and `<td>` | Success! Rows are pushed to the next page only if they would break in the middle, preserving individual cell borders. |
| CSS Classes                | Failed when using Callable Apex. The CSS was ignored.                                                                 |
| LIMIT/OFFSET               | Impractical for dynamic or user-driven row counts.                                                                    |

#### Final Recommendations for Borders

1. Inline Styling: For tables generated via Callable Apex, avoid CSS classes. Apply `style="page-break-inside: avoid"` directly to the `<tr>` or `<td>` tags.
2. sd:repeatheader: This is the best workaround for top borders. By repeating the `<thead>`, you ensure a top border exists on every new page.
   * *Note: The missing bottom border on the preceding page remains a known limitation.*
3. Component Templates (Method 2): Use Component Templates so each row renders independently. This allows row-level "avoid" logic to function most reliably.

***

#### 💡 TL;DR: Summary of What Works

* Small Tables: Use `page-break-inside: avoid` on the `<table>` or `<tbody>`.
* Row-Level Control: Apply `page-break-inside: avoid` directly to `<tr>` and `<td>` tags.
* Headers/Top Borders: Use the sd:repeatheader pattern from the KB.
* Callable Apex: Use inline styles or Method 2 components, as external/global CSS classes may not render correctly.

{% hint style="info" %}
Reference: For detailed implementation of headers, see the KB article: *Configure a Multi-Page Table to Repeat Its Header on Every Page.*
{% endhint %}

***

### Negative Currencies & RTF Table Layouts

This page covers formatting issues within S-Docs tables, specifically regarding negative currency notation and layout disruptions caused by Rich Text fields.

#### Formatting Negative Currency Values

Issue: Negative values render in accounting notation—e.g., `($500)`—instead of the standard negative sign format—e.g., `-$500`.

Solution: Add a hyphen (`-`) to the beginning of the `format-number` mask within the `<column>` tag. S-Docs recognizes this character as the indicator for negative sign formatting and will only apply it when the value is less than zero.

Correct Syntax:

HTML

```
<column prefix="$" format-number="-#,###.##">test.unitprice</column>
```

***

#### Layout Breaks with `type="rtf"`

Issue: When `type="rtf"` is applied to a table column (to handle Rich Text Area fields), the layout often breaks or `postfix` attributes (like `%`) fail to render correctly.

Cause:

S-Docs injects `<p>` (paragraph) tags when processing RTF content. These tags create block-level elements that push the `postfix` content to a new line or disrupt the table cell alignment.

Solution:

Use the `strip-html="true"` attribute on the column. This removes the injected `<p>` tags before the postfix is applied, allowing the data and the postfix to sit on the same line.

Correct Syntax:

XML

```
<column postfix="%" strip-html="true">test.unitprice</column>
```

***

#### 💡 Summary Table

| **Issue**           | **Cause**                    | **Fix**                         |
| ------------------- | ---------------------------- | ------------------------------- |
| ($100) vs -$100     | Default accounting format    | Use `format-number="-#,###.##"` |
| Broken Postfix (%)  | Injected `<p>` tags from RTF | Use `strip-html="true"`         |
| Layout Misalignment | Block-level HTML in cells    | Use `strip-html="true"`         |

{% hint style="info" %}
When dealing with Rich Text fields, `strip-html="true"` is often more reliable than `type="rtf"` if you only need the text content without the original HTML styling.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://help.sdocs.com/sdocs/template-architecture/template-authoring/table-formatting-how-to-guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
