基于ERB模板的PDF导出:列表对齐与分页问题求助
ERB模板生成PDF的排版问题解决方案
核心问题与需求
动态渲染内容通过ERB生成PDF时,遇到排版矛盾:
- 编号需与h2标题同行,标题换行后要对齐编号下方
- 标题不能跨页拆分
- 标题与对应第一段不能被分页隔开
- 尝试调整
block、page-break、list-style-position等CSS属性后,解决一个问题会引发另一个(比如改block后分页问题仍存在,用div包裹导致对齐混乱、编号全为0)
实际渲染段落的ERB代码:
<% point_content.call(key.to_s.delete_suffix('_title'), price_list_variant).each do |paragraph| %> <p><%= paragraph %></p> <% end %>
分步解决方案及原理
1. 实现编号与标题同行+换行对齐
放弃默认列表编号,改用CSS计数器+Flex布局控制:
/* 初始化计数器 */ .content-container { counter-reset: section-counter 0; } /* 标题容器:控制编号与标题布局 */ .title-wrapper { counter-increment: section-counter; display: flex; align-items: flex-start; margin: 1em 0; } /* 自定义编号 */ .title-wrapper::before { content: counter(section-counter) "."; margin-right: 0.5em; flex-shrink: 0; /* 编号固定宽度,不被挤压 */ } /* 标题样式 */ .title-wrapper h2 { flex: 1; /* 标题占满剩余宽度,换行时自动对齐编号下方 */ margin: 0; line-height: 1.5; }
原理:默认列表的list-style-position无论设inside还是outside,都无法完美解决换行对齐问题。Flex布局将编号(伪元素)和标题拆分为两个独立的Flex项,flex-shrink:0保证编号不被压缩,flex:1让标题自动填充剩余空间,换行时自然对齐编号下方。
2. 禁止标题跨页+标题与第一段不分离
将标题和对应第一段包裹在同一个容器中,配合分页控制CSS:
/* 标题+段落的分组容器,阻止内部内容跨页 */ .section-group { page-break-inside: avoid; } /* 禁止标题单独留在页尾 */ .title-wrapper h2 { page-break-after: avoid; }
调整ERB代码,将标题与对应段落的循环包裹在分组容器内:
<div class="content-container"> <% # 假设point_content是包含标题和对应段落的键值对集合 %> <% point_content.each do |title_key, paragraphs| %> <div class="section-group"> <div class="title-wrapper"> <h2><%= title_key.delete_suffix('_title') %></h2> </div> <% paragraphs.each do |paragraph| %> <p><%= paragraph %></p> <% end %> </div> <% end %> </div>
原理:page-break-inside: avoid会让浏览器尽量保持容器内的内容在同一页,避免标题和第一段被分页拆分;page-break-after: avoid防止标题刚好落在页尾,下一段被迫跑到下一页。之前用div包裹出问题,是因为没有正确设置计数器的层级(计数器未在父容器初始化),且布局逻辑混乱导致编号未正确递增。
3. 修复编号全为0的问题
确保计数器的counter-reset在所有标题容器的共同父元素上(比如上述的.content-container),同时counter-increment在每个标题容器(.title-wrapper)上。这样每个标题容器都会触发计数器递增,避免出现编号为0的情况。
内容的提问来源于stack exchange,提问作者user1128912
相关产品推荐
相关产品推荐

