Wall of People Groups: the poster's footer tells the calling to bless the nations, from Abraham to the church, with Christ the cornerstone at its centre
Every scripture, name and reference, on the printed sheet and on the canvas, is set in its own direction, so an English verse on a right-to-left page keeps its full stop at the end.
This commit is contained in:
parent
32e7e4c30c
commit
00dabc0a9e
3 changed files with 43 additions and 18 deletions
|
|
@ -41,9 +41,9 @@ export function planPoster(presetId, n) {
|
|||
const b = P.border
|
||||
const W = P.sheetW - 2 * b
|
||||
// Top of sheet 1: the title + DOXA line, then the prayer-themes table. Bottom of the last sheet: the
|
||||
// priestly blessing, the priesthood of believers and the closing line. All scale with the width.
|
||||
// priestly blessing, the journey from Abraham to the church and the closing line. All scale with the width.
|
||||
const headH = W * 0.075, tableH = W * 0.17
|
||||
const bannerH = headH + tableH, footerH = W * 0.16
|
||||
const bannerH = headH + tableH, footerH = W * 0.2
|
||||
// Each sheet's content band, in the sheet's own coordinates: y0..y1.
|
||||
const bands = []
|
||||
for (let s = 0; s < P.sheets; s++) {
|
||||
|
|
@ -81,6 +81,8 @@ export function planPoster(presetId, n) {
|
|||
|
||||
// The closing line under the footer (agent-written); translated via texts.labels.closing when available.
|
||||
const CLOSING_EN = 'Jesus Christ is the cornerstone, and we are living stones. These people groups are the reward of his suffering. Take up our priestly calling: bless them in his name, and join the global family in prayer.'
|
||||
// The journey's station names (agent-written); translated via texts.labels.journey_<id> when available.
|
||||
const JOURNEY_EN = { called: 'Called', promised: 'Promised', redeemed: 'Redeemed', cornerstone: 'The cornerstone', children: 'Children of Abraham', priesthood: 'A royal priesthood', sent: 'Sent to the lost' }
|
||||
const THEME_EN = { treasure: 'Treasure', mountain: 'Mountain', sea: 'Sea' }
|
||||
// Three simple line icons for the prayer themes (stroke = currentColor).
|
||||
const ICONS = {
|
||||
|
|
@ -111,16 +113,19 @@ export function buildBlessPosterHtml(groups, presetId = DEFAULT_PRESET, texts =
|
|||
const g = groups[c.i], v = blessingFor(c.i)
|
||||
const img = g.imageUrl ? `<img src="${esc(g.imageUrl)}" alt="">` : '<div class="noimg"></div>'
|
||||
// Below MIN_READABLE_PT the verse would print as grey noise: that size shows name + photo only.
|
||||
const verse = plan.text.readable ? `<div class="vs">${esc(v.text)}</div><div class="rf">${esc(v.ref)}</div>` : ''
|
||||
const verse = plan.text.readable ? `<div class="vs" dir="auto">${esc(v.text)}</div><div class="rf" dir="auto">${esc(v.ref)}</div>` : ''
|
||||
return `<div class="card" style="left:${f(c.x)};top:${f(c.y)};width:${f(c.w)};height:${f(c.h)}">`
|
||||
+ `<div class="nm">${esc(g.name || '')}</div>${img}${verse}</div>`
|
||||
+ `<div class="nm" dir="auto">${esc(g.name || '')}</div>${img}${verse}</div>`
|
||||
}
|
||||
const pages = plan.bands.map((bd) => {
|
||||
const s = bd.s, last = s === plan.bands.length - 1
|
||||
const glue = s > 0 ? `<div class="glue" style="top:0;height:${f(b)}">${esc(glueHere)} ▲ ${s} / ${P.sheets}</div>` : ''
|
||||
// THE STORY, top to bottom: in Jesus' name → three prayer themes (OT + NT) → every people group →
|
||||
// the priestly blessing → the priesthood of believers → the closing line, at the foundation.
|
||||
const cell = (v) => `<p class="tv">${esc(v.text)}</p><p class="tr">${esc(v.ref)}</p>`
|
||||
// the priestly blessing → the calling to bless, Abraham to the church with Christ at its centre →
|
||||
// the closing line, at the foundation. Each station's text takes dir="auto": the grid runs right to left
|
||||
// in an RTL locale, while an English verse inside it keeps its punctuation at the end. The same holds for
|
||||
// every scripture, name and reference on the sheet (cards, chart, blessing, closing).
|
||||
const cell = (v) => `<p class="tv" dir="auto">${esc(v.text)}</p><p class="tr" dir="auto">${esc(v.ref)}</p>`
|
||||
const table = `<div class="themes" dir="${dir}">`
|
||||
+ `<div class="rl rl-h"><span>${esc(lab('themesLabel', 'Prayer themes'))}</span></div>`
|
||||
+ THEMES.map((t) => `<div class="th"><div class="ic">${ICONS[t.id]}</div><div class="tn">${esc(lab('theme_' + t.id, THEME_EN[t.id]))}</div></div>`).join('')
|
||||
|
|
@ -136,9 +141,9 @@ export function buildBlessPosterHtml(groups, presetId = DEFAULT_PRESET, texts =
|
|||
+ `<div style="height:${f(plan.tableH)}">${table}</div></header>` : ''
|
||||
const foot = bd.footerY != null
|
||||
? `<footer class="ftr" dir="${dir}" style="top:${f(bd.footerY)};height:${f(plan.footerH)}">`
|
||||
+ `<div class="bless">${POSTER.blessing.lines.map((l) => `<p>${esc(l)}</p>`).join('')}<p class="br">${esc(POSTER.blessing.ref)}</p></div>`
|
||||
+ `<div class="priest">${POSTER.priesthood.map((v) => `<p>${esc(v.text)} <span>${esc(v.ref)}</span></p>`).join('')}</div>`
|
||||
+ `<p class="close">${esc(lab('closing', CLOSING_EN))}</p>`
|
||||
+ `<div class="bless">${POSTER.blessing.lines.map((l) => `<p dir="auto">${esc(l)}</p>`).join('')}<p class="br" dir="auto">${esc(POSTER.blessing.ref)}</p></div>`
|
||||
+ `<div class="jny">${POSTER.journey.map((v) => `<div class="st${v.centre ? ' ctr' : ''}"><p class="sn" dir="auto">${esc(lab('journey_' + v.id, JOURNEY_EN[v.id]))}</p><p class="sv" dir="auto">${esc(v.text)}</p><p class="sr" dir="auto">${esc(v.ref)}</p></div>`).join('')}</div>`
|
||||
+ `<p class="close" dir="auto">${esc(lab('closing', CLOSING_EN))}</p>`
|
||||
+ `</footer>` : ''
|
||||
const trim = !last && P.sheets > 1 ? '<div class="trim"></div>' : ''
|
||||
return `<section class="sheet">${glue}${head}${plan.cards.filter((c) => c.s === s).map(card).join('')}${foot}${trim}`
|
||||
|
|
@ -166,15 +171,18 @@ body{font-family:Georgia,'Times New Roman',serif;color:#1c1c1c;-webkit-print-col
|
|||
.ftr{position:absolute;left:${f(b)};width:${f(W)};display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;border-top:.03in solid #1c1c1c}
|
||||
.bless p{margin:0;font-size:${f(W * 0.0145)};line-height:1.25;font-weight:600}
|
||||
.bless .br{font-size:${f(W * 0.0075)};font-weight:400;color:#666;margin-top:${f(W * 0.002)}}
|
||||
.priest{margin-top:${f(W * 0.008)};max-width:88%}.priest p{margin:${f(W * 0.002)} 0;font-size:${f(W * 0.0068)};font-style:italic;line-height:1.3}
|
||||
.priest span{font-style:normal;color:#666;font-size:.8em}
|
||||
.jny{display:grid;grid-template-columns:repeat(7,1fr);gap:${f(W * 0.006)};margin-top:${f(W * 0.01)};width:100%;align-items:stretch}
|
||||
.st{display:flex;flex-direction:column;justify-content:center;padding:${f(W * 0.003)} ${f(W * 0.004)};border-top:.02in solid #c9b58a}
|
||||
.st .sn{margin:0 0 ${f(W * 0.002)};font:700 ${f(W * 0.0062)} Georgia,serif;letter-spacing:.05em;text-transform:uppercase;color:#6b4f1d}
|
||||
.st .sv{margin:0;font-size:${f(W * 0.0056)};font-style:italic;line-height:1.3}
|
||||
.st .sr{margin:${f(W * 0.002)} 0 0;font-size:${f(W * 0.0048)};color:#666}
|
||||
.st.ctr{background:#faf8f2;border:.03in solid #1c1c1c}.st.ctr .sv{font-size:${f(W * 0.0064)};font-weight:600}.st.ctr .sn{color:#1c1c1c}
|
||||
.close{margin:${f(W * 0.008)} auto 0;max-width:80%;font:700 ${f(W * 0.0085)}/1.35 -apple-system,'Segoe UI',Arial,sans-serif}
|
||||
.card{position:absolute;padding:${f(plan.cw * 0.035)};display:flex;flex-direction:column;align-items:center;text-align:center;overflow:hidden}
|
||||
.card .nm{font:700 ${nPt}pt/1.1 -apple-system,'Segoe UI',Arial,sans-serif;width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:none}
|
||||
.card img,.card .noimg{width:100%;height:${plan.text.readable ? 52 : 78}%;object-fit:cover;margin:${f(plan.cw * 0.02)} 0;background:#eee;flex:none}
|
||||
.card .vs{font-size:${vPt}pt;line-height:1.15;font-style:italic}
|
||||
.card .rf{font-size:${(vPt * 0.8).toFixed(1)}pt;color:#777;margin-top:1pt}
|
||||
.ftr{position:absolute;left:${f(b)};width:${f(W)};display:flex;align-items:center;justify-content:center;text-align:center}
|
||||
.glue{position:absolute;left:0;width:100%;border-bottom:.02in dashed #b00;display:flex;align-items:center;justify-content:center;font:700 ${f(Math.min(0.5, b * 0.08))} sans-serif;color:#b00;background:rgba(176,0,0,.035);letter-spacing:.05em;text-transform:uppercase}
|
||||
.trim{position:absolute;left:0;bottom:0;width:100%;border-bottom:.02in solid #999}
|
||||
.tag{position:absolute;right:${f(Math.max(0.15, b * 0.1))};top:${f(Math.max(0.1, b * 0.08))};font:600 ${f(Math.max(0.12, b * 0.035))} sans-serif;color:#aaa}`
|
||||
|
|
|
|||
|
|
@ -73,11 +73,18 @@ export const POSTER = {
|
|||
withName('{Name} lift up his face toward you, and give you peace.'),
|
||||
],
|
||||
},
|
||||
// The second footer: the priesthood of believers.
|
||||
priesthood: [
|
||||
{ ref: '1 Peter 2:5', text: 'You also as living stones are built up as a spiritual house, to be a holy priesthood, to offer up spiritual sacrifices, acceptable to God through Jesus Christ.' },
|
||||
{ ref: '1 Peter 2:9', text: 'But you are a chosen race, a royal priesthood, a holy nation, a people for God’s own possession, that you may proclaim the excellence of him who called you out of darkness into his marvelous light.' },
|
||||
],
|
||||
// The second footer: the calling to bless the nations as a journey, Abraham to the church, with Christ
|
||||
// the cornerstone at its centre (the `centre` station). Whole sentences of the WEB, joined by " … " where
|
||||
// a station quotes two of them; a verse's own closing speech mark is dropped (as on the cards).
|
||||
journey: [
|
||||
{ id: 'called', ref: 'Genesis 12:2–3', text: 'I will make of you a great nation. I will bless you and make your name great. You will be a blessing. … All the families of the earth will be blessed through you.' },
|
||||
{ id: 'promised', ref: 'Genesis 15:5–6', text: '“Look now toward the sky, and count the stars, if you are able to count them.” He said to Abram, “So your offspring will be.” He believed in {NAME}, who credited it to him for righteousness.' },
|
||||
{ id: 'redeemed', ref: 'Galatians 3:13–14', text: 'Christ redeemed us from the curse of the law, having become a curse for us. … that the blessing of Abraham might come on the Gentiles through Christ Jesus, that we might receive the promise of the Spirit through faith.' },
|
||||
{ id: 'cornerstone', centre: true, ref: '1 Peter 2:4–5', text: 'Come to him, a living stone, rejected indeed by men, but chosen by God, precious. You also as living stones are built up as a spiritual house, to be a holy priesthood, to offer up spiritual sacrifices, acceptable to God through Jesus Christ.' },
|
||||
{ id: 'children', ref: 'Galatians 3:7, 29', text: 'Know therefore that those who are of faith are children of Abraham. … If you are Christ’s, then you are Abraham’s offspring and heirs according to promise.' },
|
||||
{ id: 'priesthood', ref: '1 Peter 2:9', text: 'But you are a chosen race, a royal priesthood, a holy nation, a people for God’s own possession, that you may proclaim the excellence of him who called you out of darkness into his marvelous light.' },
|
||||
{ id: 'sent', ref: 'Luke 19:10', text: 'For the Son of Man came to seek and to save that which was lost.' },
|
||||
].map((v) => ({ ...v, text: withName(v.text) })),
|
||||
// Kept for reference: the earlier footer candidates (sheet rows 99), superseded by the Driver's choice.
|
||||
subBanner: { ref: 'Proverbs 18:21', text: 'Death and life are in the power of the tongue; those who love it will eat its fruit.' },
|
||||
footer: { ref: 'Proverbs 11:11', text: 'By the blessing of the upright, the city is exalted, but it is overthrown by the mouth of the wicked.' },
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ import { wallD3 } from './wallD3.js'
|
|||
import { wallGrid } from './wallLod.js'
|
||||
import { blessingFor, wrapText } from './blessings.js'
|
||||
|
||||
// 'rtl' when a string's first strong character is Hebrew/Arabic-script, else 'ltr' (canvas has no dir="auto").
|
||||
const RTL_FIRST = /^[^A-Za-z\u00C0-\u024F\u0370-\u052F\u0590-\u08FF\uFB1D-\uFDFF\uFE70-\uFEFF]*[\u0590-\u08FF\uFB1D-\uFDFF\uFE70-\uFEFF]/
|
||||
export const textDir = (s) => (RTL_FIRST.test(String(s || '')) ? 'rtl' : 'ltr')
|
||||
|
||||
const ICON = {
|
||||
heart: 'M12 20.5s-7.2-4.4-9.2-8.8C1.3 8.3 3.2 4.6 6.7 4.6c2 0 3.5 1.1 4.3 2.4.8-1.3 2.3-2.4 4.3-2.4 3.5 0 5.4 3.7 3.9 7.1-2 4.4-9.2 8.8-9.2 8.8z',
|
||||
comment: 'M4 4.5h16v11.5H9.5L4 20.5z',
|
||||
|
|
@ -170,6 +174,9 @@ export class WallCanvas {
|
|||
ctx.fillStyle = groupLink(g) ? '#0095f6' : '#262626'
|
||||
const name = g.name || g.post_title || this.o.labels('wall.unknownGroup')
|
||||
const lines = ctx.measureText(name).width > w * 0.95 ? wrapText(name, Math.max(6, Math.floor(name.length / 2) + 2), 2) : [name]
|
||||
// Each data string is drawn in its OWN direction, not the page's: an English verse on an RTL page
|
||||
// keeps its full stop at the end (canvas direction otherwise inherits the page's rtl).
|
||||
ctx.direction = textDir(name)
|
||||
lines.forEach((ln, k) => ctx.fillText(ln, x + w / 2, y + h * 0.06 + k * nameSize * 1.1, w * 0.96))
|
||||
if (this.mode === 'bless') {
|
||||
// The verse under the photo (blessings.js, WEB, verbatim), then its reference.
|
||||
|
|
@ -177,14 +184,17 @@ export class WallCanvas {
|
|||
const b = blessingFor(i), vl = wrapText(b.text, per, 4)
|
||||
const vy = imgY + imgH + vSize * 1.5
|
||||
ctx.font = `italic ${vSize}px Georgia, "Times New Roman", serif`; ctx.fillStyle = '#262626'
|
||||
ctx.direction = textDir(b.text)
|
||||
vl.forEach((ln, k) => ctx.fillText(ln, x + w / 2, vy + k * vSize * 1.25, w * 0.96))
|
||||
ctx.font = `${vSize * 0.82}px -apple-system, "Segoe UI", Arial, sans-serif`; ctx.fillStyle = '#8e8e8e'
|
||||
ctx.direction = textDir(b.ref)
|
||||
ctx.fillText(b.ref, x + w / 2, vy + vl.length * vSize * 1.25 + vSize * 0.4, w * 0.96)
|
||||
return
|
||||
}
|
||||
// SOCIAL: status, then heart / comment / share, the prayer count and "view prayers".
|
||||
ctx.font = `${Math.max(3, w * 0.055)}px -apple-system, "Segoe UI", Arial, sans-serif`; ctx.fillStyle = '#8e8e8e'
|
||||
if (g.status) ctx.fillText(g.status, x + w / 2, y + h * 0.14, w * 0.96)
|
||||
if (g.status) { ctx.direction = textDir(g.status); ctx.fillText(g.status, x + w / 2, y + h * 0.14, w * 0.96) }
|
||||
ctx.direction = 'inherit' // the translated counters below follow the page
|
||||
const L = this.socialLayout(R)
|
||||
const count = this.o.prayCount(g)
|
||||
ctx.lineWidth = 1.8; ctx.lineJoin = 'round'; ctx.lineCap = 'round'
|
||||
|
|
|
|||
Loading…
Reference in a new issue