Subtables ignore page breaks and bleed over margins
Closed this issue · 9 comments
I'm using the following (rather complex) code to generate a table which contains another table in a cell:
def risk_control_matrix
document.bounding_box(
[
document.bounds.left,
document.bounds.top - 50
],
width: document.bounds.width,
height: document.bounds.height - 100
) do
document.font 'Helvetica'
document.text(
'Risk Control Matrix',
size: 14,
style: :bold,
align: :center
)
document.move_down(20)
risks = [
[
'Risk Title',
'Description',
'In. Imp.',
'In. Prob.',
'In. Risk',
'Control Suite',
'Res. Imp.',
'Res. Prob.',
'Res. Risk'
]
]
ratings = export.account.ratings.where(ratings_type: 'score').order('ratings_value DESC').collect{ |rating| { :name => rating.name, :colour => rating.ratings_colour } }
assessment_rcm_risks = export.assessment.risks.active.includes(:residual_rating).where('ratings.ratings_value < 100').sort_by{ |x| x.residual_rating.ratings_value }.reverse!
assessment_rcm_risks += export.assessment.risks.active.includes(:residual_rating).where('ratings.ratings_value = 100')
risks += assessment_rcm_risks.map do |risk|
controls = [
[
'Control Title',
'Mitigation',
'Design',
'Performance',
'Effectiveness'
]
]
controls += risk.controls.map do |control|
mitigation = control.mitigations.find(:first, conditions: ['risk_id = ?', risk.id])
[
control.title,
mitigation.mitigation_strength,
mitigation.design_score,
mitigation.performance_score,
mitigation.effectiveness_score
]
end
inner_table = document.make_table(controls, header: true, position: :center) do
row(0).style(
background_color: '9acd32',
align: :center,
text_color: 'ffffff',
size: 7
)
column(0).style(
border_widths: 0.25,
padding: [5, 3],
size: 7,
width: 60
)
column(1).style(
border_widths: 0.25,
padding: [5, 3],
size: 7,
width: 35
)
column(2).style(
border_widths: 0.25,
padding: [5, 3],
size: 7,
width: 32
)
column(3).style(
border_widths: 0.25,
padding: [5, 3],
size: 7,
width: 45
)
column(4).style(
border_widths: 0.25,
padding: [5, 3],
size: 7,
width: 48
)
values = cells.columns(1..-1).rows(1..-1)
low = values.filter do |cell|
cell.content.to_s == 'Low'
end
low.background_color = '000000'
low.text_color = 'ffffff'
ineffective = values.filter do |cell|
cell.content.to_s == 'Ineffective'
end
ineffective.background_color = '000000'
ineffective.text_color = 'ffffff'
medium = values.filter do |cell|
cell.content.to_s == 'Medium'
end
medium.background_color = '9e9e9e'
medium.text_color = 'ffffff'
partiallyeffective = values.filter do |cell|
cell.content.to_s == 'Partially effective'
end
partiallyeffective.background_color = '9e9e9e'
partiallyeffective.text_color = 'ffffff'
end
if risk.controls.present?
[
risk.title,
risk.description,
risk.inherent_impact_rating.name,
risk.inherent_probability_rating.name,
risk.inherent_rating.name.chars.first.capitalize,
inner_table,
risk.residual_impact_rating.name,
risk.residual_probability_rating.name,
risk.residual_rating.name.chars.first.capitalize
]
else
[
risk.title,
risk.description,
risk.inherent_impact_rating.name,
risk.inherent_probability_rating.name,
risk.inherent_rating.name.chars.first.capitalize,
'',
risk.residual_impact_rating.name,
risk.residual_probability_rating.name,
risk.residual_rating.name.chars.first.capitalize
]
end
end
document.table(risks, header: true, position: :center, ) do
column(0).style(
border_widths: 0.25,
padding: [5, 3],
size: 8,
width: 100
)
column(1).style(
border_widths: 0.25,
padding: [5, 3],
size: 8,
width: 120
)
column(2).style(
border_widths: 0.25,
padding: [5, 3],
size: 8,
width: 50
)
column(3).style(
border_widths: 0.25,
padding: [5, 3],
size: 8,
width: 45
)
column(4).style(
border_widths: 0.25,
padding: [5, 3],
size: 8
)
column(5).style(
border_widths: 0.25,
padding: [0, 0],
width: 220
)
column(6).style(
border_widths: 0.25,
padding: [5, 3],
size: 8,
width: 50
)
column(7).style(
border_widths: 0.25,
padding: [5, 3],
size: 8,
width: 45
)
column(8).style(
border_widths: 0.25,
padding: [5, 3],
size: 8
)
row(0).style(
background_color: '1e90ff',
align: :center,
padding: [5,3],
text_color: 'ffffff',
size: 8
)
ratings.each do |rating|
values = cells.columns(1..-1).rows(1..-1)
specific_cell = values.filter do |cell|
cell.content.to_s == rating[:name].chars.first.capitalize || cell.content.to_s == "#{rating[:name].chars.first.capitalize} (!)"
end
specific_cell.background_color = 'FF0000' if rating[:colour] == 'badge-important'
specific_cell.text_color = 'ffffff' if rating[:colour] == 'badge-important'
specific_cell.background_color = 'FF9933' if rating[:colour] == 'badge-warning'
specific_cell.background_color = '00CC00' if rating[:colour] == 'badge-success'
end
end
end
end
However, the table produced doesn't respect page breaks and bleeds over the margins (other tables without subtable respect page breaks fine):
Sorry, should've added, this is using v0.2.1
Hi guys, is this Gem still live? Just let me know if not and I'll find another way of dealing with the subtable issue.
@betjaminrichards yes, actually @hbrandl worked on this issue extensively here: #3 but was unable to resolve it.
His latest progress is here: 7aa6f30
It's a very complex issue, I was helping him test and we went through around ~5 iterations and there were still edge case bugs before we gave up.
@johnnyshields thanks for your response. We'll check it out and see whether it fixes our issue or whether we're one of the edge cases!
@betjaminrichards yeah the fix is a work in progress.
Looking at your table (and knowing how prawn works currently) I think the best option here is for you to restructure your code so as to eliminate subtables--i.e. move to a flat single parent table structure--and then limit the number of cells you're merging vertically, something like the attached image. Merged cells do not break across pages currently.
If you are interested to fund a proper fix to this issue please get in touch with me (i.e. a bounty), I would be potentially be willing to chip in as well.
@johnnyshields yes, would be interested, but it depends on the ball park...
And thanks for the tip on the tables. Will refactor.
Please contact me via email (johnny.shields@gmail.com). Closing this issue because it's a dupe of an existing issue.
Sorry, that it took me so long to reply.
@johnnyshields summed it up pretty nicely. I tried to fix the underlying problem / enhance the code and failed at doing so after lots of hours.
The quest you'd have to solve is quite difficult. I'm gonna add a final comment to pull request #31 indicating my problems in a minute.
In short: prawn-table can't do what you need it to do. I'm sorry about it, but it's not easily fixable.