-
Notifications
You must be signed in to change notification settings - Fork 73
feat: support rendering of column line separators #2088
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -725,7 +725,7 @@ export function layoutDocument(blocks: FlowBlock[], measures: Measure[], options | |
|
|
||
| // Track active and pending columns | ||
| let activeColumns = options.columns ?? { count: 1, gap: 0 }; | ||
| let pendingColumns: { count: number; gap: number } | null = null; | ||
| let pendingColumns: { count: number; gap: number; withSeparator?: boolean } | null = null; | ||
|
|
||
| // Track active and pending orientation | ||
| let activeOrientation: 'portrait' | 'landscape' | null = null; | ||
|
|
@@ -822,11 +822,15 @@ export function layoutDocument(blocks: FlowBlock[], measures: Measure[], options | |
| // Update columns - if section has columns, use them; if undefined, reset to single column. | ||
| // In OOXML, absence of <w:cols> means single column (default). | ||
| if (block.columns) { | ||
| next.activeColumns = { count: block.columns.count, gap: block.columns.gap }; | ||
| next.activeColumns = { | ||
| count: block.columns.count, | ||
| gap: block.columns.gap, | ||
| withSeparator: block.columns.withSeparator, | ||
| }; | ||
| next.pendingColumns = null; | ||
| } else { | ||
| // No columns specified = reset to single column (OOXML default) | ||
| next.activeColumns = { count: 1, gap: 0 }; | ||
| next.activeColumns = { count: 1, gap: 0, withSeparator: false }; | ||
| next.pendingColumns = null; | ||
| } | ||
| // Schedule section refs for first section (will be applied on first page creation) | ||
|
|
@@ -932,7 +936,9 @@ export function layoutDocument(blocks: FlowBlock[], measures: Measure[], options | |
| } | ||
| // Helper to get column config: use block.columns if defined, otherwise reset to single column (OOXML default) | ||
| const getColumnConfig = () => | ||
| block.columns ? { count: block.columns.count, gap: block.columns.gap } : { count: 1, gap: 0 }; | ||
| block.columns | ||
| ? { count: block.columns.count, gap: block.columns.gap, withSeparator: block.columns.withSeparator } | ||
| : { count: 1, gap: 0, withSeparator: false }; | ||
|
|
||
| if (block.attrs?.requirePageBoundary) { | ||
| next.pendingColumns = getColumnConfig(); | ||
|
|
@@ -972,6 +978,11 @@ export function layoutDocument(blocks: FlowBlock[], measures: Measure[], options | |
| if (activeOrientation) { | ||
| page.orientation = activeOrientation; | ||
| } | ||
|
|
||
| if (activeColumns.count > 1) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. heads up on an edge case here: unlikely to hit in real documents, but the fix is straightforward -- either set |
||
| page.columns = { count: activeColumns.count, gap: activeColumns.gap, withSeparator: activeColumns.withSeparator }; | ||
| } | ||
|
|
||
| // Set vertical alignment from active section state | ||
| if (activeVAlign && activeVAlign !== 'top') { | ||
| page.vAlign = activeVAlign; | ||
|
|
@@ -1307,7 +1318,10 @@ export function layoutDocument(blocks: FlowBlock[], measures: Measure[], options | |
| const advanceColumn = paginator.advanceColumn; | ||
|
|
||
| // Start a new mid-page region with different column configuration | ||
| const startMidPageRegion = (state: PageState, newColumns: { count: number; gap: number }): void => { | ||
| const startMidPageRegion = ( | ||
| state: PageState, | ||
| newColumns: { count: number; gap: number; withSeparator?: boolean }, | ||
| ): void => { | ||
| // Record the boundary at current Y position | ||
| const boundary: ConstraintBoundary = { | ||
| y: state.cursorY, | ||
|
|
@@ -2288,7 +2302,10 @@ export function layoutDocument(blocks: FlowBlock[], measures: Measure[], options | |
| // after processing sections. Page/region-specific column changes are encoded | ||
| // implicitly via fragment positions. Consumers should not assume this is | ||
| // a static document-wide value. | ||
| columns: activeColumns.count > 1 ? { count: activeColumns.count, gap: activeColumns.gap } : undefined, | ||
| columns: | ||
| activeColumns.count > 1 | ||
| ? { count: activeColumns.count, gap: activeColumns.gap, withSeparator: activeColumns.withSeparator } | ||
| : undefined, | ||
| }; | ||
| } | ||
|
|
||
|
|
@@ -2498,19 +2515,22 @@ function normalizeColumns(input: ColumnLayout | undefined, contentWidth: number) | |
| const gap = Math.max(0, input?.gap ?? 0); | ||
| const totalGap = gap * (count - 1); | ||
| const width = (contentWidth - totalGap) / count; | ||
| const withSeparator = input?.withSeparator ?? false; | ||
|
|
||
| if (width <= COLUMN_EPSILON) { | ||
| return { | ||
| count: 1, | ||
| gap: 0, | ||
| width: contentWidth, | ||
| withSeparator, | ||
| }; | ||
| } | ||
|
|
||
| return { | ||
| count, | ||
| gap, | ||
| width, | ||
| withSeparator, | ||
| }; | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,7 @@ export type NormalizedColumns = ColumnLayout & { width: number }; | |
|
|
||
| export type ConstraintBoundary = { | ||
| y: number; | ||
| columns: { count: number; gap: number }; | ||
| columns: { count: number; gap: number; withSeparator?: boolean }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you'll notice the type using it in the files you touched would mean the next person adding a field (like you did with |
||
| }; | ||
|
|
||
| export type PageState = { | ||
|
|
@@ -27,7 +27,7 @@ export type PaginatorOptions = { | |
| getActiveFooterDistance(): number; | ||
| getActivePageSize(): { w: number; h: number }; | ||
| getDefaultPageSize(): { w: number; h: number }; | ||
| getActiveColumns(): { count: number; gap: number }; | ||
| getActiveColumns(): { count: number; gap: number; withSeparator?: boolean }; | ||
| getCurrentColumns(): NormalizedColumns; | ||
| createPage(number: number, pageMargins: PageMargins, pageSizeOverride?: { w: number; h: number }): Page; | ||
| onNewPage?: (state: PageState) => void; | ||
|
|
@@ -37,7 +37,7 @@ export function createPaginator(opts: PaginatorOptions) { | |
| const states: PageState[] = []; | ||
| const pages: Page[] = []; | ||
|
|
||
| const getActiveColumnsForState = (state: PageState): { count: number; gap: number } => { | ||
| const getActiveColumnsForState = (state: PageState): { count: number; gap: number; withSeparator?: boolean } => { | ||
| if (state.activeConstraintIndex >= 0 && state.constraintBoundaries[state.activeConstraintIndex]) { | ||
| return state.constraintBoundaries[state.activeConstraintIndex].columns; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this fallback
{ count: 1, gap: 0, withSeparator: false }shows up 6 times acrossincrementalLayout.tsandindex.ts. there's already aSINGLE_COLUMN_DEFAULTconstant insection-breaks.tsthat represents the same thing, but it's not exported.if you export it and reuse it here, the next field added won't require hunting down 6 scattered literals -- which is exactly the situation you ran into when adding
withSeparator.