# Um ensaio de metaciência em inglês e espanhol

> O ensaio de Ioannidis de 2005 recomposto a partir de um só script em duas edições, com fórmulas no texto e nas células e a Figura 1 traçada pela equação.

- Versão HTML: https://postext.dev/pt/cookbook/bilingual-metascience-essay
- Receita Nº 141 · Tabelas · Nível 3 (Avançado) · Saídas: Canvas, PDF
- Gêneros: Artigos e trabalhos acadêmicos
- Requer postext ≥ 1.19.0, postext-pdf ≥ 1.19.0 · testada com 1.19.1, postext-pdf 1.19.1 em 2026-10-06
- Páginas: [1](https://postext.dev/cookbook/bilingual-metascience-essay/en/p01.webp?v=00c27ea1), [2](https://postext.dev/cookbook/bilingual-metascience-essay/en/p02.webp?v=00c27ea1), [3](https://postext.dev/cookbook/bilingual-metascience-essay/en/p03.webp?v=00c27ea1), [5](https://postext.dev/cookbook/bilingual-metascience-essay/en/p05.webp?v=00c27ea1), [6](https://postext.dev/cookbook/bilingual-metascience-essay/en/p06.webp?v=00c27ea1), [7](https://postext.dev/cookbook/bilingual-metascience-essay/en/p07.webp?v=00c27ea1)
- PDF: https://postext.dev/cookbook/bilingual-metascience-essay/en/bilingual-metascience-essay.pdf?v=00c27ea1
- Abrir no Sandbox: https://postext.dev/pt/sandbox#recipe=bilingual-metascience-essay&lang=en (.postext: https://postext.dev/cookbook/bilingual-metascience-essay/en/bilingual-metascience-essay.postext)
- Última atualização: 2026-10-06
- Outros idiomas: [en](https://postext.dev/en/cookbook/bilingual-metascience-essay.md), [es](https://postext.dev/es/cookbook/bilingual-metascience-essay.md), [ca](https://postext.dev/ca/cookbook/bilingual-metascience-essay.md), [zh](https://postext.dev/zh/cookbook/bilingual-metascience-essay.md), [ja](https://postext.dev/ja/cookbook/bilingual-metascience-essay.md), [ar](https://postext.dev/ar/cookbook/bilingual-metascience-essay.md)

## Em poucas palavras

Um ensaio famoso sobre por que muitos resultados de pesquisa não se confirmam, composto como separata de sete páginas. Um só script gera a edição inglesa e uma tradução espanhola, cada qual com hifenização, vírgula decimal e rótulos.

## O que você vai compor

Uma separata de sete páginas do ensaio de John Ioannidis *Why Most Published Research Findings Are False* (PLoS Medicine, 2005), resumido e composto numa página de 210 × 280 mm em duas colunas. Uma faixa azul-marinho abre a primeira página, com as curvas da própria fórmula do ensaio desenhadas em tom tênue atrás do título. As três fórmulas são destacadas e numeradas; as tabelas 2×2 têm uma fórmula em cada célula, com α e β de verdade; a Figura 1 é calculada pela Eq. (2), e a última coluna da Tabela 4 pela mesma equação. O mesmo script compõe uma tradução espanhola com hifenização espanhola, vírgula decimal nas tabelas e figuras, aspas « » e rótulos em espanhol. Fecham o texto 33 referências no estilo Vancouver, refeitas em BibTeX a partir da lista do artigo.

**Esta receita responde a:**

- Como faço para publicar um artigo em duas línguas a partir de um só script, com a tipografia de cada língua?
- Como faço uma tabela com linhas de cabeçalho, células mescladas, larguras de coluna e alinhamento por célula?
- Como componho matemática (em linha, em destaque, equações) e a mantenho vetorial no PDF?
- Como recomponho com o Postext um artigo de acesso aberto do arXiv ou do PubMed Central, mantendo citações, figuras e linha de licença?

## A resposta curta

```js
// script.js, linhas 31–54
// The Cookbook composes the pen once per edition: content.<LANG>.md and each named slot
// replace the @content markers. What else follows the language is set here.
const edition = {
  // Hyphenation patterns and the words the engine writes (Tabla, Continúa) follow the
  // locale, an exact code (gotcha: hyphenation-locales).
  locale: t({ en: 'en-us', es: 'es' }),
  // Table 1 / Tabla 1, Figure 1 / Figura 1, counted through the essay; tables caption above.
  resourceTypes: defaultResourceTypes(LANG).map((type) => ({ ...type, shortLabel: type.name,
    numberingTemplate: '{n}', resetOn: 'never',
    ...(type.id === 'table' && { captionStyle: { position: 'above' } }) })),
  // Vancouver numbers in brackets, [2–4]; the CSL locale writes the list's words (2nd ed.,
  // 2.ª ed.). The titles of the works stay in English in both editions.
  citations: { style: 'elsevier-vancouver', locale: t({ en: 'en-US', es: 'es-ES' }),
    marker: 'brackets', collapseRanges: true,
    bibliography: { fontSize: em(0.78), lineHeight: pt(9.4), entrySpacing: pt(0.8),
      labelWidth: mm(6.4), doi: 'hide' } },
};
// The numbers the script writes (Table 4's PPV, the figure axes) take the edition's decimal
// sign, 0.85 or 0,85. Formulas keep their symbols; content.es.md writes 0{,}05 inside $…$
// so that TeX sets no space after the comma.
const number = (x, digits = 2) => x.toLocaleString(t({ en: 'en-US', es: 'es-ES' }),
  { minimumFractionDigits: digits, maximumFractionDigits: digits });
registerCitationEngine(createCiteprocEngine({ styles: STYLES, locales: LOCALES }));
await initMathEngine(); // before the tables are built (gotcha: math-bundle)
```

## Ingredientes

**Ensina**

- [Matemática](https://postext.dev/pt/docs/document-format.md#fórmulas-matemáticas): LaTeX no texto e em destaque composto pelo MathJax, sobre a grade e vetorial em todas as saídas, com química pelo mhchem.
- [Tabelas a partir de dados](https://postext.dev/pt/docs/document-format.md#inserção-em-bloco-opcional-posicionamento-explícito-em-linha): Tabelas como recursos, com linhas de cabeçalho, células mescladas, proporções de coluna, alinhamento por célula e listas dentro das células; tabelas com barras verticais do Markdown não são interpretadas.
- [Hifenização e idioma do documento](https://postext.dev/pt/docs/justification.md#idiomas-compatíveis): Hifenização com padrões do TeX em oito idiomas, escolhida pelo locale do documento, que também define os textos de continuação das tabelas e o idioma do PDF com tags.

**Também usa**

- [Estilo de tabela](https://postext.dev/pt/docs/configuration.md#estilo-de-tabela)
- [Figura e Tabela no seu idioma](https://postext.dev/pt/docs/configuration.md#tipos-de-recurso)
- [Citações em um estilo de citação](https://postext.dev/pt/docs/document-format.md#citações-e-bibliografia)
- [Bibliografia a partir das referências](https://postext.dev/pt/docs/document-format.md#citações-e-bibliografia)
- [Figuras e tabelas como recursos](https://postext.dev/pt/docs/document-format.md#recursos)
- [Citações que posicionam as figuras](https://postext.dev/pt/docs/document-format.md#referência-em-linha-a-forma-principal)
- [Legendas numeradas](https://postext.dev/pt/docs/document-format.md#numeração-pela-primeira-referência)
- [Posicionamento de figuras](https://postext.dev/pt/docs/document-format.md#posicionamento)
- [Estilo de legenda](https://postext.dev/pt/docs/configuration.md#estilo-de-legenda)
- [Aberturas desenhadas](https://postext.dev/pt/docs/configuration.md#largura-e-design-avançado)
- [Atributos de título](https://postext.dev/pt/docs/document-format.md#atributos-de-título)
- [Boxes na largura da página](https://postext.dev/pt/docs/configuration.md#o-contêiner-callout)
- [Boxes](https://postext.dev/pt/docs/configuration.md#estilos-de-boxe)
- [Chips no texto](https://postext.dev/pt/docs/configuration.md#estilos-de-chip)
- [Cabeços e fólios](https://postext.dev/pt/docs/configuration.md#cabeços-e-rodapés)
- [Exportação para PDF](https://postext.dev/pt/docs/configuration.md#geração-de-pdf)
- [Colunas dentro de um boxe](https://postext.dev/pt/docs/document-format.md#columns)
- [Estilos de título](https://postext.dev/pt/docs/configuration.md#estilos-de-título)
- [Cabeços por tipo de página](https://postext.dev/pt/docs/configuration.md#elementos-de-texto)
- [Estilos de parágrafo](https://postext.dev/pt/docs/configuration.md#estilos-de-parágrafo)
- [Fontes incorporadas ao PDF](https://postext.dev/pt/docs/configuration.md#por-que-um-provedor-de-fontes)
- [Tipos de recurso personalizados](https://postext.dev/pt/docs/configuration.md#tipos-de-recurso)
- [Sobrescritos e subscritos](https://postext.dev/pt/docs/document-format.md#formatação-em-linha)
- [Capítulos sem número](https://postext.dev/pt/docs/configuration.md#estilos-de-título)

**A configuração em resumo**

- [`bodyText`](https://postext.dev/pt/docs/configuration.md#texto-do-corpo), [`calloutStyles`](https://postext.dev/pt/docs/configuration.md#estilos-de-boxe), [`captionStyle`](https://postext.dev/pt/docs/configuration.md#estilo-de-legenda), [`chipStyles`](https://postext.dev/pt/docs/configuration.md#estilos-de-chip), [`citations`](https://postext.dev/pt/docs/configuration.md#citações), [`colorPalette`](https://postext.dev/pt/docs/configuration.md#paleta-de-cores), [`footer`](https://postext.dev/pt/docs/configuration.md#cabeços-e-rodapés), [`header`](https://postext.dev/pt/docs/configuration.md#cabeços-e-rodapés), [`headingStyles`](https://postext.dev/pt/docs/configuration.md#estilos-de-título), [`headings`](https://postext.dev/pt/docs/configuration.md#títulos), [`layout`](https://postext.dev/pt/docs/configuration.md#diagramação), [`locale`](https://postext.dev/pt/docs/configuration.md#hifenização), [`math`](https://postext.dev/pt/docs/configuration.md#matemática), [`page`](https://postext.dev/pt/docs/configuration.md#página), [`paragraphStyles`](https://postext.dev/pt/docs/configuration.md#estilos-de-parágrafo), [`resourceTypes`](https://postext.dev/pt/docs/configuration.md#tipos-de-recurso), [`tableStyle`](https://postext.dev/pt/docs/configuration.md#estilo-de-tabela)

**API**

- [`LOCALES`](https://postext.dev/pt/docs/document-format.md#citações-e-bibliografia), [`STYLES`](https://postext.dev/pt/docs/document-format.md#citações-e-bibliografia), [`buildDocument`](https://postext.dev/pt/docs/configuration.md#compilar-um-documento), [`clearMeasurementCache`](https://postext.dev/pt/docs/configuration.md#cache-de-medidas), [`createCiteprocEngine`](https://postext.dev/pt/docs/document-format.md#citações-e-bibliografia), [`decompressWoff2`](https://postext.dev/pt/docs/configuration.md#provedor-de-fontes-no-navegador-fontsource--woff2), [`defaultResourceTypes`](https://postext.dev/pt/docs/configuration.md#tipos-de-recurso), [`initMathEngine`](https://postext.dev/pt/docs/document-format.md#fórmulas-matemáticas), [`mergeCells`](https://postext.dev/pt/docs/document-format.md#inserção-em-bloco-opcional-posicionamento-explícito-em-linha), [`parseTSV`](https://postext.dev/pt/docs/document-format.md#inserção-em-bloco-opcional-posicionamento-explícito-em-linha), [`registerCitationEngine`](https://postext.dev/pt/docs/document-format.md#citações-e-bibliografia), [`registerResourceImage`](https://postext.dev/pt/docs/architecture.md#superfície-da-api), [`renderPageToCanvas`](https://postext.dev/pt/docs/configuration.md#renderizar-uma-página-como-bitmap), [`renderToPdf`](https://postext.dev/pt/docs/configuration.md#geração-de-pdf), [`setAlignment`](https://postext.dev/pt/docs/document-format.md#inserção-em-bloco-opcional-posicionamento-explícito-em-linha)

**Tipos**

- Gelasio (OFL-1.1), Sofia Sans Semi Condensed (OFL-1.1)

## Preparo

### 1 · Um script, duas edições

O código está [na resposta curta](https://postext.dev/pt/cookbook/bilingual-metascience-essay.md#a-resposta-curta), mais acima. As Receitas montam o script uma vez para cada língua e põem `content.en.md` ou `content.es.md` onde está a marca `@content`; cada espaço nomeado (`content.corollaries.es.md`, `content.resources.es.md`) segue a mesma regra, e um espaço sem arquivo em espanhol usa o inglês, e é assim que as duas edições compartilham um único arquivo BibTeX. Todo o resto que muda com a língua passa por `LANG`: `locale` escolhe os padrões de hifenização (`'es'` é o código que hifeniza o espanhol), `defaultResourceTypes(LANG)` escreve *Tabla* e *Figura*, a localidade CSL escreve as palavras da lista de referências, e `toLocaleString` dá vírgula decimal aos números que o script calcula. A tradução segue a ortotipografia espanhola: aspas « », *0,05* no texto e `0{,}05` dentro de uma fórmula, para que o TeX não trate a vírgula como pontuação e ponha um espaço depois dela. Para publicar um artigo seu em duas línguas, mantenha um arquivo por língua para o texto e as legendas, e reúna qualquer outra diferença num único objeto como `edition`.

### 2 · Fórmulas dentro das células

```js
// script.js, linhas 400–425
// A cell, a caption or a note sets $…$ as the text does (postext ≥ 1.19): MathJax paths at
// the cell's 8 pt, on the baseline of its line and aligned with the cell, vector in the PDF.
// The faces' latin files have no α or β (gotcha: latin-subset); the formulas need none.
const [CELL_PT, PAD] = [8, 1]; // cell type (pt), cell padding (mm)
function tableModel(tsv, widths, headerRows, merges = [], right = []) {
  let model = { ...parseTSV(tsv, { headerRows }), columnWidths: widths };
  model.rows.forEach((_, row) => right.forEach((c) => {
    model = setAlignment(model, { row, col: c }, 'right'); // the numbers, the PPV and their heads
  }));
  for (const range of merges) model = mergeCells(model, range); // gotcha: merged-cells-hiddenby
  return model;
}
// "Research finding" over both header rows, "True relationship" over Yes, No and Total.
const twoByTwo = (tsv) => tableModel(tsv, [1.1, 1.6, 1.6, 2.5], 2, [
  { start: { row: 0, col: 0 }, end: { row: 1, col: 0 } },
  { start: { row: 0, col: 1 }, end: { row: 0, col: 3 } }]);
// Table 4's last column is computed from Eq. (2), α = 0.05: two significant figures, as in 2005.
const ppv = (power, R, u, alpha = 0.05) => (power * R + u * (1 - power) * R)
  / (R + alpha - (1 - power) * R + u - u * alpha + u * (1 - power) * R);
const read = (s) => Number(s.replace(',', '.'));
const odds = (s) => s.split(':').map((x) => Number(x.replace(/\D/g, ''))).reduce((a, b) => a / b);
const ppvRows = (tsv) => tsv.split('\n').map((line, r) => {
  const [power, R, u, example, head] = line.split('\t');
  const p = ppv(read(power), odds(R), read(u)); // 0.0010: as many decimals as 2 figures need
  return [power, R, u, example, r ? number(p, 1 - Math.floor(Math.log10(p))) : head].join('\t');
}).join('\n');
```

Desde o postext 1.19, uma célula de tabela compõe `$…$` como o texto, então uma célula que no TSV diz `$c(1-\beta)R/(R+1)$` é uma fórmula: traçados do MathJax no corpo de 8 pt das células, na linha de base da linha da célula, de modo que *Yes*, *No* e as fórmulas ficam na mesma linha, e contornos vetoriais no PDF. Os arquivos latin da Fontsource não têm letras gregas, e as fórmulas não precisam delas. Legendas e notas também aceitam matemática: a legenda da Tabela 4 chama o poder de 1 − β, e a nota dela supõe α = 0,05, como o artigo de 2005 imprime. `setAlignment` alinha à direita os números da Tabela 4, e `mergeCells` junta *Research finding* sobre as duas linhas de cabeçalho e *True relationship* sobre três colunas; ele também escreve as células ocultas de que uma célula mesclada precisa.

### 3 · Legendas e tabelas do espaço nomeado, números da fórmula

```js
// script.js, linhas 429–457
const parsed = blocks.trim().split(/\n\s*\n/).map((block) => {
  const fields = {};
  const tsv = block.split('\n').filter((line) => {
    const m = /^(id|caption|note|alt): (.*)$/.exec(line);
    if (m) fields[m[1]] = m[2];
    return !m;
  }).join('\n');
  return { ...fields, tsv };
});
const tables = parsed.filter((b) => b.tsv).map(({ id, caption, note, tsv }) => {
  const model = id === 'tbl-ppv'
    ? tableModel(ppvRows(tsv), [0.8, 0.95, 0.75, 3.8, 1.05], 1, [], [0, 1, 2, 4])
    : twoByTwo(tsv);
  return { id, typeId: 'table', kind: 'table', createdAt: 0, updatedAt: 0, caption, note,
    table: { model }, placement: { position: 'auto', ...(id !== 'tbl-ppv' && { span: 'page' }) } };
});
const figures = parsed.filter((b) => !b.tsv).map(({ id, caption, note, alt }) => ({ id,
  typeId: 'figure', kind: 'svg', createdAt: 0, updatedAt: 0, caption, note, altText: alt,
  placement: { position: 'auto', span: 'page' },
  svg: { fileId: `${id}.svg`, width: FIG_W * 10, height: FIG_H * 10 } }));
const resources = [...tables, ...figures,
  { id: 'band-art', typeId: 'figure', kind: 'svg', createdAt: 0, updatedAt: 0,
    svg: { fileId: 'band-art.svg', width: 1400, height: BAND * 10 } }];
const tableStyle = { rules: 'horizontal', borderColor: col('rule'), borderWidth: pt(0.5),
  headerBackground: col('navy'), headerColor: col('paper'), headerBold: true,
  headerFontFamily: SANS, headerFontSize: pt(CELL_PT), bodyFontFamily: SANS,
  bodyFontSize: pt(CELL_PT), bodyColor: col('ink'), cellPadding: mm(PAD) };
const captionStyle = { fontFamily: SANS, fontSize: pt(8.2), color: col('ink'), labelBold: true,
  labelColor: col('accent'), gap: mm(2), note: { fontSize: pt(7), color: col('muted') } };
```

As tabelas, legendas, notas e textos alternativos ficam em `content.resources.<lang>.md`, um bloco para cada um, de modo que quem traduz só mexe em arquivos de texto, e cada edição leva só a própria língua. A coluna de PPV da Tabela 4 não é digitada: `ppv()` a calcula com o poder, *R* e *u* de cada linha, e os nove valores batem com os impressos em 2005 até dois algarismos significativos. As tabelas flutuam com `position: 'auto'`, e assim a Tabela 1 pode ocupar o pé da página que a cita em vez de se acumular com os outros flutuantes na página seguinte.

### 4 · A Figura 1 traçada pela Eq. (2)

```js
// script.js, linhas 461–520
const R2 = (x) => Math.round(x * 100) / 100;
// An SVG drawn as a picture cannot use the page's web fonts (gotcha: svg-no-webfonts): the
// figures carry the label face inline under its own name, which the PDF asks the provider for.
async function inlineFace(family, weight) {
  const id = family.toLowerCase().replace(/\s+/g, '-');
  const url = `https://cdn.jsdelivr.net/npm/@fontsource/${id}@5/files/${id}-latin-${weight}`
    + '-normal.woff2';
  const bytes = new Uint8Array(await (await fetch(url)).arrayBuffer());
  let bin = '';
  for (const b of bytes) bin += String.fromCharCode(b);
  return `@font-face{font-family:'${family}';font-weight:${weight};`
    + `src:url(data:font/woff2;base64,${btoa(bin)}) format('woff2')}`;
}
const mix = (f) => `#${[1, 3, 5].map((i) => Math.round(parseInt(palette.navy.slice(i, i + 2), 16)
  * (1 - f) + parseInt(palette.accent.slice(i, i + 2), 16) * f).toString(16).padStart(2, '0'))
  .join('')}`; // navy for the first curve, vermilion for the last
const label = (x, y, s, size, extra = '', fill = palette.muted) => `<text x="${R2(x)}" `
  + `y="${R2(y)}" font-size="${size}" font-family="${SANS}" fill="${fill}" ${extra}>${s}</text>`;
const line = (points, stroke, width, extra = '') => `<path d="M${points.map(([x, y]) =>
  `${R2(x)} ${R2(y)}`).join('L')}" fill="none" stroke="${stroke}" stroke-width="${width}" `
  + `${extra}/>`;
// Three panels, power 0.80, 0.50 and 0.20; PPV in % against R from 0 to 1, one curve per value.
function panels(face, curve, values, name, digits, dashed) {
  const [pw, top, plotH, left] = [52, 12, 34, 8]; // panel width, plot top and height, y labels
  let out = `<style>${face}</style>`;
  [0.8, 0.5, 0.2].forEach((power, p) => {
    const x0 = p * (pw + (FIG_W - 3 * pw) / 2) + left;
    const X = (R) => x0 + R * (pw - left - 2);
    const Y = (v) => top + plotH * (1 - v);
    const trace = (f) => Array.from({ length: 101 }, (_, k) => [X(k / 100), Y(f(k / 100))]);
    out += label(x0 - left, 4, `${'ABC'[p]}  <tspan font-weight="400">${t({ en: 'Power',
      es: 'Potencia' })} ${number(power)}</tspan>`, 3.4, '', palette.navy);
    if (!p) out += label(x0 - left, top - 4, t({ en: 'PPV (%)', es: 'VPP (%)' }), 2.8);
    for (const v of [0, 0.2, 0.4, 0.6, 0.8, 1]) {
      out += line([[X(0), Y(v)], [X(1), Y(v)]], palette.rule, v ? 0.15 : 0.3)
        + label(X(0) - 1.4, Y(v) + 1, v * 100, 2.6, 'text-anchor="end"')
        + label(X(v), Y(0) + 3.6, number(v, v % 1 ? 1 : 0), 2.6, 'text-anchor="middle"');
    }
    if (dashed) out += line(trace((R) => dashed(power, R)), palette.muted, 0.35,
      'stroke-dasharray="1 0.8"');
    values.forEach((value, i) => {
      out += line(trace((R) => curve(power, R, value)), mix(i / (values.length - 1)), 0.6);
    });
    out += label(X(0.5), Y(0) + 8, `${t({ en: 'Pre-study odds', es: 'Razón previa' })}, `
      + '<tspan font-style="italic">R</tspan>', 2.9, 'text-anchor="middle"');
  });
  const key = values.map((value, i) => line([[48 + i * 24, 57.8], [54 + i * 24, 57.8]],
    mix(i / (values.length - 1)), 0.8) + label(56 + i * 24, 58.8, `<tspan font-style="italic">`
    + `${name}</tspan> = ${number(value, digits)}`, 2.9)).join('');
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${FIG_W * 10}" height="${FIG_H * 10}" `
    + `viewBox="0 0 ${FIG_W} ${FIG_H}">${out}${key}</svg>`;
}
// Eq. (3): n independent studies of equal power, no bias.
const teams = (power, R, n, alpha = 0.05) => (R * (1 - (1 - power) ** n))
  / (R + 1 - (1 - alpha) ** n - R * (1 - power) ** n);
const bandArt = () => `<svg xmlns="http://www.w3.org/2000/svg" width="1400" height="${BAND * 10}" `
  + `viewBox="0 0 140 ${BAND}">${Array.from({ length: 10 }, (_, i) => line(Array.from(
    { length: 81 },
    (_, k) => [10 + k * 1.6, 92 - 76 * ppv(0.8, k / 80, i / 10)]), palette.mist, 0.5,
  `stroke-opacity="${R2(0.5 - i * 0.04)}"`)).join('')}</svg>`;
```

Cada painel é um laço sobre *R* de 0 a 1 que passa pela mesma `ppv()` que preenche a Tabela 4. Traçar as curvas pela fórmula revela algo que a figura de 2005 esconde: as curvas dela correspondem a *u* = 0; 0,05; 0,20 e 0,80, enquanto a legenda diz 0,05; 0,20; 0,50 e 0,80. Esta versão desenha os valores da legenda, acrescenta a curva sem viés em tracejado, e a nota da legenda explica isso. Os rótulos são texto na fonte dos rótulos: o SVG leva a fonte como `@font-face` embutido para o canvas, e o PDF os compõe na fonte que o provedor fornece.

### 5 · Uma primeira página e um boxe para um ensaio

```js
// script.js, linhas 58–81
const text = (id, content, family, size, color, placement, extra) => ({ kind: 'text', id,
  content, fontFamily: family, fontSize: pt(size), color: col(color), align: 'left',
  overflow: 'wrap', placement, ...extra });
const at = (to, edge, x, y, width) => ({ anchor: { to, edge }, offset: { x: mm(x), y: mm(y) },
  ...(width && { size: { width: mm(width), height: 'auto' } }) });
const caps = (size, fontWeight = 700) => ({ fontWeight, letterSpacing: pt(size * 0.18),
  textTransform: 'uppercase' });
const BAND = 100; // mm from the top of the trim to the foot of the band
const titleBlock = { enabled: true, minHeight: mm(BAND + 12 - TOP), slot: { elements: [
  { kind: 'box', id: 'band', style: { backgroundColor: col('navy') },
    placement: { anchor: { to: 'bleed', edge: 'top-left' },
      size: { width: 'fill', height: mm(BAND + 3) } } }, // + the 3 mm bleed
  { kind: 'image', id: 'curves', resourceId: 'band-art', // Eq. (2), faint, for u = 0 to 0.9
    placement: at('page', 'top-left', 128, 46, 76) }, // under the title, clear of the text
  text('kicker', '{attr.kicker}', SANS, 8, 'mist', at('page', 'top-left', INNER, 20, 120),
    caps(8)),
  text('title', '{titleText}', SANS, 36, 'paper', at('#kicker', 'below', 0, 6, 150),
    { fontWeight: 800, lineHeight: 1.02 }),
  text('author', '{attr.author}', SERIF, 13, 'paper', at('#title', 'below', 0, 8, 150)),
  text('affiliation', '{attr.affiliation}', SANS, 8, 'mist', at('#author', 'below', 0, 1.6, 150),
    { lineHeight: 1.35 }),
  text('source', '{attr.source}', SANS, 7.4, 'muted', at('page', 'top-left', INNER, BAND + 4,
    MEASURE), { lineHeight: 1.35 }),
] } };
```

O título é o único de nível 1; o estilo dele, `essay`, ocupa a largura da página e desenha a faixa, o chapéu, o autor, as filiações e a linha da fonte a partir dos atributos do título, então a edição espanhola só troca palavras. O resumo é um boxe na largura da página sobre um fundo de cor. O Boxe 1, o exemplo resolvido, flutua para o pé de uma página atravessando as duas colunas e compõe o texto em duas colunas dentro do boxe. Os corolários mantêm a forma do artigo, com o enunciado em negrito no começo do parágrafo: `:chip[Corollary 1]{style="corollary"}` põe antes de cada um um pequeno rótulo vermelhão.

```js
// script.js, linhas 126–147
const calloutStyles = [
  { id: 'summary', span: 'page', background: col('tint'), marginTop: pt(0),
    marginBottom: pt(LEAD), padding: { top: mm(4), right: mm(6), bottom: mm(4), left: mm(6) },
    titleStyle: { fontFamily: SANS, fontSize: pt(8), ...caps(8), color: col('accent'),
      gap: mm(1.5) },
    body: { fontSize: pt(9.4), lineHeight: pt(13), firstLineIndent: pt(0) } },
  { id: 'box', span: 'page', placement: 'bottom', columnGap: mm(GUTTER),
    backgroundEnabled: false, border: { enabled: false },
    stripe: { enabled: true, side: 'top', width: pt(2.5), color: col('navy') },
    padding: { top: mm(2.5), right: mm(0), bottom: mm(1), left: mm(0) },
    marginTop: pt(LEAD), marginBottom: pt(LEAD),
    titleStyle: { fontFamily: SANS, fontSize: pt(9), fontWeight: 700, color: col('navy'),
      gap: mm(1.2) },
    body: { fontFamily: SANS, fontSize: pt(8.4), lineHeight: pt(11.2), firstLineIndent: mm(3) } },
];
// :chip[Corollary 1]{style="corollary"}: a label in the sans and the accent, no frame.
const chipStyles = [{ id: 'corollary', fontFamily: SANS, fontSize: em(0.86), bold: true,
  color: col('accent'), backgroundEnabled: false, borderWidth: pt(0), paddingX: em(0),
  gap: em(0.35) }];
const paragraphStyles = [{ id: 'colophon', fontFamily: SANS, fontSize: pt(7.6),
  lineHeight: pt(10.4), color: col('muted'), boldColor: col('ink'), textAlign: 'left',
  firstLineIndent: pt(0), spaceBetween: pt(3) }];
```

## A receita completa

Um único arquivo, composto a partir da pasta da receita com o texto de exemplo e o kit comum das Receitas já incluídos; ele monta a própria página. Para executá-lo, coloque-o em um `<script type="module">` de uma página vazia ou cole-o no painel JS de um pen novo do CodePen (como módulo). Ele importa o postext do esm.sh, então não há nada para instalar nem compilar.

- Pasta da receita: https://github.com/drnachio/postext/tree/main/cookbook/bilingual-metascience-essay

### script.js

```js
// ═══ Postext Cookbook · Nº 141 · A metascience essay in English and Spanish ═══════
// https://postext.dev/en/cookbook/bilingual-metascience-essay
// Code: MIT · Text: J. P. A. Ioannidis, PLoS Med 2005 (CC BY) · Figures: drawn in code (CC BY 4.0)
// Fonts: Gelasio, Sofia Sans Semi Condensed (SIL OFL 1.1) · Needs postext ≥ 1.19.0
import {
  buildDocument, renderPageToCanvas, clearMeasurementCache, registerResourceImage,
  registerCitationEngine, defaultResourceTypes, initMathEngine, parseTSV, mergeCells, setAlignment,
} from 'https://esm.sh/postext?bundle';
import { renderToPdf, decompressWoff2 } from 'https://esm.sh/postext-pdf';
import { createCiteprocEngine, STYLES, LOCALES } from 'https://esm.sh/postext-citeproc';

const LANG = 'en'; // @lang: the language of the sample document ('en' | 'es')
const RECIPE = 'bilingual-metascience-essay';

// ─── 1 · Design ─────────────────────────────────────────────────────────────
// #region palette: a navy for the furniture, one vermilion accent, every colour linked
const palette = { ink: '#1c1d24', navy: '#22305a', accent: '#b8442a', tint: '#eceff5',
  rule: '#b4bccb', muted: '#5a6070', mist: '#c3cde6', paper: '#ffffff' };
const col = (id) => ({ hex: palette[id], model: 'hex', paletteId: id });
const colorPalette = Object.entries({ ...palette, 'main-color': palette.navy })
  .map(([id, hex]) => ({ id, name: id, value: { hex, model: 'hex' } }));
// #endregion
const [SERIF, SANS] = ['Gelasio', 'Sofia Sans Semi Condensed'];
// mm: the 210 × 280 trim, head, foot, inner and outer margins, and the gutter
const [TRIM_W, TRIM_H, TOP, BOTTOM, INNER, OUTER, GUTTER] = [210, 280, 22, 22, 19, 17, 6];
const MEASURE = TRIM_W - INNER - OUTER; // 174 mm across both columns
const [BODY, LEAD] = [9.4, 13]; // pt
const [FIG_W, FIG_H] = [MEASURE, 60]; // mm: Figures 1 and 2, across both columns

// #region answer: one script, two editions: LANG picks the text, the language and the numbers
// The Cookbook composes the pen once per edition: content.<LANG>.md and each named slot
// replace the @content markers. What else follows the language is set here.
const edition = {
  // Hyphenation patterns and the words the engine writes (Tabla, Continúa) follow the
  // locale, an exact code (gotcha: hyphenation-locales).
  locale: t({ en: 'en-us', es: 'es' }),
  // Table 1 / Tabla 1, Figure 1 / Figura 1, counted through the essay; tables caption above.
  resourceTypes: defaultResourceTypes(LANG).map((type) => ({ ...type, shortLabel: type.name,
    numberingTemplate: '{n}', resetOn: 'never',
    ...(type.id === 'table' && { captionStyle: { position: 'above' } }) })),
  // Vancouver numbers in brackets, [2–4]; the CSL locale writes the list's words (2nd ed.,
  // 2.ª ed.). The titles of the works stay in English in both editions.
  citations: { style: 'elsevier-vancouver', locale: t({ en: 'en-US', es: 'es-ES' }),
    marker: 'brackets', collapseRanges: true,
    bibliography: { fontSize: em(0.78), lineHeight: pt(9.4), entrySpacing: pt(0.8),
      labelWidth: mm(6.4), doi: 'hide' } },
};
// The numbers the script writes (Table 4's PPV, the figure axes) take the edition's decimal
// sign, 0.85 or 0,85. Formulas keep their symbols; content.es.md writes 0{,}05 inside $…$
// so that TeX sets no space after the comma.
const number = (x, digits = 2) => x.toLocaleString(t({ en: 'en-US', es: 'es-ES' }),
  { minimumFractionDigits: digits, maximumFractionDigits: digits });
registerCitationEngine(createCiteprocEngine({ styles: STYLES, locales: LOCALES }));
await initMathEngine(); // before the tables are built (gotcha: math-bundle)
// #endregion

// #region title: the essay's first page: a navy band with the title, author and source
const text = (id, content, family, size, color, placement, extra) => ({ kind: 'text', id,
  content, fontFamily: family, fontSize: pt(size), color: col(color), align: 'left',
  overflow: 'wrap', placement, ...extra });
const at = (to, edge, x, y, width) => ({ anchor: { to, edge }, offset: { x: mm(x), y: mm(y) },
  ...(width && { size: { width: mm(width), height: 'auto' } }) });
const caps = (size, fontWeight = 700) => ({ fontWeight, letterSpacing: pt(size * 0.18),
  textTransform: 'uppercase' });
const BAND = 100; // mm from the top of the trim to the foot of the band
const titleBlock = { enabled: true, minHeight: mm(BAND + 12 - TOP), slot: { elements: [
  { kind: 'box', id: 'band', style: { backgroundColor: col('navy') },
    placement: { anchor: { to: 'bleed', edge: 'top-left' },
      size: { width: 'fill', height: mm(BAND + 3) } } }, // + the 3 mm bleed
  { kind: 'image', id: 'curves', resourceId: 'band-art', // Eq. (2), faint, for u = 0 to 0.9
    placement: at('page', 'top-left', 128, 46, 76) }, // under the title, clear of the text
  text('kicker', '{attr.kicker}', SANS, 8, 'mist', at('page', 'top-left', INNER, 20, 120),
    caps(8)),
  text('title', '{titleText}', SANS, 36, 'paper', at('#kicker', 'below', 0, 6, 150),
    { fontWeight: 800, lineHeight: 1.02 }),
  text('author', '{attr.author}', SERIF, 13, 'paper', at('#title', 'below', 0, 8, 150)),
  text('affiliation', '{attr.affiliation}', SANS, 8, 'mist', at('#author', 'below', 0, 1.6, 150),
    { lineHeight: 1.35 }),
  text('source', '{attr.source}', SANS, 7.4, 'muted', at('page', 'top-left', INNER, BAND + 4,
    MEASURE), { lineHeight: 1.35 }),
] } };
// #endregion

// Running heads 13 mm from the trim; the first page has its folio at the foot instead.
const head = (id, content, parity, edge, x, extra) => text(id, content, SANS, 7.5, 'muted',
  at('page', `top-${edge}`, x, 13), { overflow: 'clip', ...caps(7.5, 600), align: edge,
    parity, pages: 'body', ...extra });
const folio = { fontWeight: 800, color: col('accent'), letterSpacing: pt(0.4) };
const header = { elements: [
  head('v-folio', '{pageNumber}', 'even', 'left', OUTER, folio),
  head('v-title', 'Ioannidis · PLoS Medicine 2005', 'even', 'left', OUTER + 9),
  head('r-title', t({ en: 'Why most published research findings are false',
    es: 'Por qué la mayoría de los resultados publicados son falsos' }), 'odd', 'right',
  -(OUTER + 9)),
  head('r-folio', '{pageNumber}', 'odd', 'right', -OUTER, folio),
] };
const footer = { elements: [text('drop-folio', '{pageNumber}', SANS, 7.5, 'accent',
  at('page', 'bottom-right', -OUTER, -12), { ...folio, align: 'right', pages: 'opener',
    overflow: 'clip' })] };

const config = () => ({ // a factory: the engine caches resolved configs per object
  ...edition, colorPalette, header, footer,
  page: { sizePreset: 'custom', width: mm(TRIM_W), height: mm(TRIM_H), dpi: 150,
    margins: { top: mm(TOP), bottom: mm(BOTTOM), left: mm(INNER), right: mm(OUTER),
      mirror: true } },
  layout: { layoutType: 'double', gutterWidth: mm(GUTTER) },
  bodyText: { fontFamily: SERIF, fontSize: pt(BODY), lineHeight: pt(LEAD), color: col('ink'),
    boldColor: col('ink'), italicColor: col('ink'), referenceColor: col('ink'),
    referenceBold: false, textAlign: 'justify', firstLineIndent: mm(4),
    indentAfterHeading: false, hyphenation: { enabled: true }, optimalLineBreaking: true,
    avoidWidows: true, avoidOrphans: true, avoidRunts: true },
  math: { marginTop: pt(LEAD / 2), marginBottom: pt(LEAD / 2), keepWithLeadIn: true },
  headings: { fontFamily: SANS, color: col('navy'), fontWeight: 700, levels: [
    { level: 1, breakBefore: { enabled: true, parity: 'any' } }, // gotcha: headings-drop-h1-break
    { level: 2, fontSize: pt(12), lineHeight: pt(LEAD), marginTop: pt(LEAD),
      marginBottom: pt(LEAD / 2) },
  ] },
  headingStyles: [
    { id: 'essay', numbered: false, span: 'page', advancedDesign: titleBlock },
    { id: 'back', fontSize: pt(8), ...caps(8), color: col('accent'), marginBottom: pt(4) },
  ],
  calloutStyles, chipStyles, paragraphStyles, tableStyle, captionStyle,
});

// #region boxes: the summary across the page, Box 1 under a stripe, run-in corollary labels
const calloutStyles = [
  { id: 'summary', span: 'page', background: col('tint'), marginTop: pt(0),
    marginBottom: pt(LEAD), padding: { top: mm(4), right: mm(6), bottom: mm(4), left: mm(6) },
    titleStyle: { fontFamily: SANS, fontSize: pt(8), ...caps(8), color: col('accent'),
      gap: mm(1.5) },
    body: { fontSize: pt(9.4), lineHeight: pt(13), firstLineIndent: pt(0) } },
  { id: 'box', span: 'page', placement: 'bottom', columnGap: mm(GUTTER),
    backgroundEnabled: false, border: { enabled: false },
    stripe: { enabled: true, side: 'top', width: pt(2.5), color: col('navy') },
    padding: { top: mm(2.5), right: mm(0), bottom: mm(1), left: mm(0) },
    marginTop: pt(LEAD), marginBottom: pt(LEAD),
    titleStyle: { fontFamily: SANS, fontSize: pt(9), fontWeight: 700, color: col('navy'),
      gap: mm(1.2) },
    body: { fontFamily: SANS, fontSize: pt(8.4), lineHeight: pt(11.2), firstLineIndent: mm(3) } },
];
// :chip[Corollary 1]{style="corollary"}: a label in the sans and the accent, no frame.
const chipStyles = [{ id: 'corollary', fontFamily: SANS, fontSize: em(0.86), bold: true,
  color: col('accent'), backgroundEnabled: false, borderWidth: pt(0), paddingX: em(0),
  gap: em(0.35) }];
const paragraphStyles = [{ id: 'colophon', fontFamily: SANS, fontSize: pt(7.6),
  lineHeight: pt(10.4), color: col('muted'), boldColor: col('ink'), textAlign: 'left',
  firstLineIndent: pt(0), spaceBetween: pt(3) }];
// #endregion

// ─── 2 · Content ────────────────────────────────────────────────────────────
const markdown = [
  String.raw`---
title: "Why Most Published Research Findings Are False"
author: "John P. A. Ioannidis"
---

# Why Most Published Research Findings Are False {style="essay" kicker="Essay · Research methods" author="John P. A. Ioannidis" affiliation="University of Ioannina School of Medicine, Ioannina, Greece\nTufts University School of Medicine, Boston, Massachusetts, United States" source="Originally published in PLoS Medicine 2(8): e124, 30 August 2005 · doi:10.1371/journal.pmed.0020124 · © 2005 John P. A. Ioannidis, Creative Commons Attribution License"}

:::callout{type="summary" title="Summary"}
There is increasing concern that most current published research findings are false. The probability that a research claim is true may depend on study power and bias, the number of other studies on the same question, and, importantly, the ratio of true to no relationships among the relationships probed in each scientific field. In this framework, a research finding is less likely to be true when the studies conducted in a field are smaller; when effect sizes are smaller; when there is a greater number and lesser preselection of tested relationships; where there is greater flexibility in designs, definitions, outcomes, and analytical modes; when there is greater financial and other interest and prejudice; and when more teams are involved in a scientific field in chase of statistical significance. Simulations show that for most study designs and settings, it is more likely for a research claim to be false than true. Moreover, for many current scientific fields, claimed research findings may often be simply accurate measures of the prevailing bias. In this essay, I discuss the implications of these problems for the conduct and interpretation of research.
:::

Published research findings are sometimes refuted by subsequent evidence, with ensuing confusion and disappointment. Refutation and controversy is seen across the range of research designs, from clinical trials and traditional epidemiological studies [@ioannidis2001a; @lawlor2004; @vandenbroucke2004] to the most modern molecular research [@michiels2005; @ioannidis2001b]. There is increasing concern that in modern research, false findings may be the majority or even the vast majority of published research claims [@colhoun2003; @ioannidis2003; @ioannidis2005a]. However, this should not be surprising. It can be proven that most claimed research findings are false. Here I will examine the key factors that influence this problem and some corollaries thereof.


## Modeling the Framework for False Positive Findings

Several methodologists have pointed out [@sterne2001; @wacholder2004; @risch2000] that the high rate of nonreplication (lack of confirmation) of research discoveries is a consequence of the convenient, yet ill-founded strategy of claiming conclusive research findings solely on the basis of a single study assessed by formal statistical significance, typically for a *p*-value less than 0.05.

As has been shown previously, the probability that a research finding is indeed true depends on the prior probability of it being true (before doing the study), the statistical power of the study, and the level of statistical significance [@wacholder2004; @risch2000]. Consider a 2 × 2 table in which research findings are compared against the gold standard of true relationships in a scientific field. In a research field both true and false hypotheses can be made about the presence of relationships. Let $R$ be the ratio of the number of “true relationships” to “no relationships” among those tested in the field. $R$ is characteristic of the field and can vary a lot depending on whether the field targets highly likely relationships or searches for only one or a few true relationships among thousands and millions of hypotheses that may be postulated. Let us also consider, for computational simplicity, circumscribed fields where either there is only one true relationship (among many that can be hypothesized) or the power is similar to find any of the several existing true relationships. The pre-study probability of a relationship being true is $R/(R+1)$. The probability of a study finding a true relationship reflects the power $1-\beta$ (one minus the Type II error rate). The probability of claiming a relationship when none truly exists reflects the Type I error rate, $\alpha$. Assuming that $c$ relationships are being probed in the field, the expected values of the 2 × 2 table are given in :ref{id="tbl-truth"}. After a research finding has been claimed based on achieving formal statistical significance, the post-study probability that it is true is the positive predictive value, PPV. The PPV is also the complementary probability of what Wacholder et al. have called the false positive report probability [@wacholder2004]. According to the 2 × 2 table, one gets
$$\mathrm{PPV} = \frac{(1-\beta)R}{R-\beta R+\alpha}. \tag{1}$$
A research finding is thus more likely true than false if $(1-\beta)R > \alpha$. Since usually the vast majority of investigators depend on $\alpha = 0.05$, this means that a research finding is more likely true than false if $(1-\beta)R > 0.05$.

What is less well appreciated is that bias and the extent of repeated independent testing by different teams of investigators around the globe may further distort this picture and may lead to even smaller probabilities of the research findings being indeed true. We will try to model these two factors in the context of similar 2 × 2 tables.

## Bias

First, let us define bias as the combination of various design, data, analysis, and presentation factors that tend to produce research findings when they should not be produced. Let $u$ be the proportion of probed analyses that would not have been “research findings,” but nevertheless end up presented and reported as such, because of bias. Bias should not be confused with chance variability that causes some findings to be false by chance even though the study design, data, analysis, and presentation are perfect. Bias can entail manipulation in the analysis or reporting of findings. Selective or distorted reporting is a typical form of such bias. We may assume that $u$ does not depend on whether a true relationship exists or not. This is not an unreasonable assumption, since typically it is impossible to know which relationships are indeed true. In the presence of bias (:ref{id="tbl-bias"}), one gets
$$\mathrm{PPV} = \frac{[1-\beta]R+u\beta R}{R+\alpha-\beta R+u-u\alpha+u\beta R}, \tag{2}$$
and PPV decreases with increasing $u$, unless $1-\beta \le \alpha$, i.e., $1-\beta \le 0.05$ for most situations. Thus, with increasing bias, the chances that a research finding is true diminish considerably. This is shown for different levels of power and for different pre-study odds in :ref{id="fig-bias"}.

## Testing by Several Independent Teams

Several independent teams may be addressing the same sets of research questions. As research efforts are globalized, it is practically the rule that several research teams, often dozens of them, may probe the same or similar questions. The probability that at least one study, among several done on the same question, claims a statistically significant research finding is easy to estimate. For $n$ independent studies of equal power, the 2 × 2 table is shown in :ref{id="tbl-teams"}:
$$\mathrm{PPV} = \frac{R(1-\beta^n)}{R+1-[1-\alpha]^n-R\beta^n} \tag{3}$$
(not considering bias). With increasing number of independent studies, PPV tends to decrease, unless $1-\beta < \alpha$, i.e., typically $1-\beta < 0.05$. This is shown for different levels of power and for different pre-study odds in :ref{id="fig-teams"}. For $n$ studies of different power, the term $\beta^n$ is replaced by the product of the terms $\beta_i$ for $i = 1$ to $n$, but inferences are similar.
`, // title, summary, the model, bias, several teams
  String.raw`## Corollaries

A practical example is shown in Box 1. Based on the above considerations, one may deduce several interesting corollaries about the probability that a research finding is indeed true.

:::callout{type="box" title="Box 1. An Example: Science at Low Pre-Study Odds"}
:::columns{count=2}
Let us assume that a team of investigators performs a whole genome association study to test whether any of 100,000 gene polymorphisms are associated with susceptibility to schizophrenia. Based on what we know about the extent of heritability of the disease, it is reasonable to expect that probably around ten gene polymorphisms among those tested would be truly associated with schizophrenia, with relatively similar odds ratios around 1.3 for the ten or so polymorphisms and with a fairly similar power to identify any of them. Then $R = 10/100{,}000 = 10^{-4}$, and the pre-study probability for any polymorphism to be associated with schizophrenia is also $R/(R+1) = 10^{-4}$. Let us also suppose that the study has 60% power to find an association with an odds ratio of 1.3 at $\alpha = 0.05$. Then it can be estimated that if a statistically significant association is found with the *p*-value barely crossing the 0.05 threshold, the post-study probability that this is true increases about 12-fold compared with the pre-study probability, but it is still only $12 \times 10^{-4}$.

Now let us suppose that the investigators manipulate their design, analyses, and reporting so as to make more relationships cross the *p* = 0.05 threshold even though this would not have been crossed with a perfectly adhered to design and analysis and with perfect comprehensive reporting of the results, strictly according to the original study plan. In the presence of bias with $u = 0.10$, the post-study probability that a research finding is true is only $4.4 \times 10^{-4}$. Furthermore, even in the absence of any bias, when ten independent research teams perform similar experiments around the world, if one of them finds a formally statistically significant association, the probability that the research finding is true is only $1.5 \times 10^{-4}$, hardly any higher than the probability we had before any of this extensive research was undertaken!
:::
:::

:chip[Corollary 1]{style="corollary"} **The smaller the studies conducted in a scientific field, the less likely the research findings are to be true.** Small sample size means smaller power and, for all functions above, the PPV for a true research finding decreases as power decreases towards $1-\beta = 0.05$. Thus, other factors being equal, research findings are more likely true in scientific fields that undertake large studies, such as randomized controlled trials in cardiology (several thousand subjects randomized) [@yusuf1984] than in scientific fields with small studies, such as most research of molecular predictors (sample sizes 100-fold smaller) [@altman2000].

:chip[Corollary 2]{style="corollary"} **The smaller the effect sizes in a scientific field, the less likely the research findings are to be true.** Power is also related to the effect size. Thus research findings are more likely true in scientific fields with large effects, such as the impact of smoking on cancer or cardiovascular disease (relative risks 3–20), than in scientific fields where postulated effects are small, such as genetic risk factors for multigenetic diseases (relative risks 1.1–1.5) [@ioannidis2003]. Modern epidemiology is increasingly obliged to target smaller effect sizes [@taubes1995]. Consequently, the proportion of true research findings is expected to decrease.

:chip[Corollary 3]{style="corollary"} **The greater the number and the lesser the selection of tested relationships in a scientific field, the less likely the research findings are to be true.** As shown above, the post-study probability that a finding is true (PPV) depends a lot on the pre-study odds ($R$). Thus, research findings are more likely true in confirmatory designs, such as large phase III randomized controlled trials, or meta-analyses thereof, than in hypothesis-generating experiments. Fields considered highly informative and creative given the wealth of the assembled and tested information, such as microarrays and other high-throughput discovery-oriented research [@michiels2005; @ioannidis2005a; @golub1999], should have extremely low PPV.

:chip[Corollary 4]{style="corollary"} **The greater the flexibility in designs, definitions, outcomes, and analytical modes in a scientific field, the less likely the research findings are to be true.** Flexibility increases the potential for transforming what would be “negative” results into “positive” results, i.e., bias, $u$. For several research designs, e.g., randomized controlled trials [@moher2001; @ioannidis2004; @ich1999] or meta-analyses [@moher1999; @stroup2000], there have been efforts to standardize their conduct and reporting. Adherence to common standards is likely to increase the proportion of true findings. Regardless, even in the most stringent research designs, bias seems to be a major problem. For example, there is strong evidence that selective outcome reporting, with manipulation of the outcomes and analyses reported, is a common problem even for randomized trials [@chan2004]. Simply abolishing selective publication would not make this problem go away.

:chip[Corollary 5]{style="corollary"} **The greater the financial and other interests and prejudices in a scientific field, the less likely the research findings are to be true.** Conflicts of interest and prejudice may increase bias, $u$. Conflicts of interest are very common in biomedical research [@krimsky1998], and typically they are inadequately and sparsely reported [@krimsky1998; @papanikolaou2001]. Prejudice may not necessarily have financial roots. Scientists in a given field may be prejudiced purely because of their belief in a scientific theory or commitment to their own findings. Prestigious investigators may suppress via the peer review process the appearance and dissemination of findings that refute their findings, thus condemning their field to perpetuate false dogma. Empirical evidence on expert opinion shows that it is extremely unreliable [@antman1992].

:chip[Corollary 6]{style="corollary"} **The hotter a scientific field (with more scientific teams involved), the less likely the research findings are to be true.** This seemingly paradoxical corollary follows because, as stated above, the PPV of isolated findings decreases when many teams of investigators are involved in the same field. This may explain why we occasionally see major excitement followed rapidly by severe disappointments in fields that draw wide attention. The term Proteus phenomenon has been coined to describe this phenomenon of rapidly alternating extreme research claims and extremely opposite refutations [@ioannidis2005b]. Empirical evidence suggests that this sequence of extreme opposites is very common in molecular genetics [@ioannidis2005b].
`, // Box 1 and the six corollaries
  String.raw`## Most Research Findings Are False for Most Research Designs and for Most Fields

In the described framework, a PPV exceeding 50% is quite difficult to get. :ref{id="tbl-ppv"} provides the results of simulations using the formulas developed for the influence of power, ratio of true to non-true relationships, and bias, for various types of situations that may be characteristic of specific study designs and settings. A finding from a well-conducted, adequately powered randomized controlled trial starting with a 50% pre-study chance that the intervention is effective is eventually true about 85% of the time. A fairly similar performance is expected of a confirmatory meta-analysis of good-quality randomized trials: potential bias probably increases, but power and pre-test chances are higher compared to a single randomized trial. Conversely, a meta-analytic finding from inconclusive studies where pooling is used to “correct” the low power of single studies, is probably false if $R \le 1{:}3$. Research findings from underpowered, early-phase clinical trials would be true about one in four times, or even less frequently if bias is present. Epidemiological studies of an exploratory nature perform even worse, especially when underpowered, but even well-powered epidemiological studies may have only a one in five chance being true, if $R = 1{:}10$. Finally, in discovery-oriented research with massive testing, where tested relationships exceed true ones 1,000-fold (e.g., 30,000 genes tested, of which 30 may be the true culprits) [@ntzani2003; @ransohoff2004], PPV for each claimed relationship is extremely low, even with considerable standardization of laboratory and statistical methods, outcomes, and reporting thereof to minimize bias.

## Claimed Research Findings May Often Be Simply Accurate Measures of the Prevailing Bias

As shown, the majority of modern biomedical research is operating in areas with very low pre- and post-study probability for true findings. Let us suppose that in a research field there are no true findings at all to be discovered. In such a “null field,” one would ideally expect all observed effect sizes to vary by chance around the null in the absence of bias. The extent that observed findings deviate from what is expected by chance alone would be simply a pure measure of the prevailing bias.

For example, let us suppose that no nutrients or dietary patterns are actually important determinants for the risk of developing a specific tumor. Let us also suppose that the scientific literature has examined 60 nutrients and claims all of them to be related to the risk of developing this tumor with relative risks in the range of 1.2 to 1.4 for the comparison of the upper to lower intake tertiles. Then the claimed effect sizes are simply measuring nothing else but the net bias that has been involved in the generation of this scientific literature. Claimed effect sizes are in fact the most accurate estimates of the net bias. It even follows that between “null fields,” the fields that claim stronger effects (often with accompanying claims of medical or public health importance) are simply those that have sustained the worst biases.

For fields with very low PPV, the few true relationships would not distort this overall picture much. Even if a few relationships are true, the shape of the distribution of the observed effects would still yield a clear measure of the biases involved in the field. This concept totally reverses the way we view scientific results. Traditionally, investigators have viewed large and highly significant effects with excitement, as signs of important discoveries. Too large and too highly significant effects may actually be more likely to be signs of large bias in most fields of modern research. They should lead investigators to careful critical thinking about what might have gone wrong with their data, analyses, and results.

## How Can We Improve the Situation?

Is it unavoidable that most research findings are false, or can we improve the situation? A major problem is that it is impossible to know with 100% certainty what the truth is in any research question. In this regard, the pure “gold” standard is unattainable. However, there are several approaches to improve the post-study probability.

Better powered evidence, e.g., large studies or low-bias meta-analyses, may help, as it comes closer to the unknown “gold” standard. However, large studies may still have biases and these should be acknowledged and avoided. Moreover, one should be cautious that extremely large studies may be more likely to find a formally statistical significant difference for a trivial effect that is not really meaningfully different from the null [@lindley1957; @bartlett1957; @senn2001].

Second, most research questions are addressed by many teams, and it is misleading to emphasize the statistically significant findings of any single team. What matters is the totality of the evidence. Diminishing bias through enhanced research standards and curtailing of prejudices may also help. However, this may require a change in scientific mentality that might be difficult to achieve. In some research designs, efforts may also be more successful with upfront registration of studies, e.g., randomized trials [@deangelis2004]. Registration would pose a challenge for hypothesis-generating research. Some kind of registration or networking of data collections or investigators within fields may be more feasible than registration of each and every hypothesis-generating experiment. Regardless, even if we do not see a great deal of progress with registration of studies in other fields, the principles of developing and adhering to a protocol could be more widely borrowed from randomized controlled trials.

Finally, instead of chasing statistical significance, we should improve our understanding of the range of $R$ values—the pre-study odds—where research efforts operate [@wacholder2004]. Before running an experiment, investigators should consider what they believe the chances are that they are testing a true rather than a non-true relationship. Speculated high $R$ values may sometimes then be ascertained. As described above, whenever ethically acceptable, large studies with minimal bias should be performed on research findings that are considered relatively established, to see how often they are indeed confirmed. I suspect several established “classics” will fail the test [@ioannidis2005c].

Nevertheless, most new discoveries will continue to stem from hypothesis-generating research with low or very low pre-study odds. We should then acknowledge that statistical significance testing in the report of a single study gives only a partial picture, without knowing how much testing has been done outside the report and in the relevant field at large. Despite a large statistical literature for multiple testing corrections [@hsueh2003], usually it is impossible to decipher how much data dredging by the reporting authors or other research teams has preceded a reported research finding. Even if determining this were feasible, this would not inform us about the pre-study odds. Thus, it is unavoidable that one should make approximate assumptions on how many relationships are expected to be true among those probed across the relevant research fields and research designs. Even though these assumptions would be considerably subjective, they would still be very useful in interpreting research claims and putting them in context.

## Article information {style="back"}

:::paragraphs{style="colophon"}
**Competing interests.** The author has declared that no competing interests exist. **Abbreviation.** PPV, positive predictive value.

**This edition.** Abridged from J. P. A. Ioannidis, “Why Most Published Research Findings Are False”, *PLoS Medicine* 2(8): e124 (2005), doi:10.1371/journal.pmed.0020124, under the Creative Commons Attribution License. Some sentences and paragraphs are left out, the formulas are numbered, the tables and figures are redrawn and one typing slip is corrected. Set in Gelasio and Sofia Sans Semi Condensed (SIL OFL); formulas by MathJax.
:::

## References {style="back"}

:::bibliography{title=""}
`, // the last three sections and the back matter
  String.raw`:::references{format=bibtex}
@article{ioannidis2001a, author = {Ioannidis, J. P. and Haidich, A. B. and Lau, J.},
  title = {Any casualties in the clash of randomised and observational evidence?},
  journal = {BMJ}, year = 2001, volume = 322, pages = {879--880}}
@article{lawlor2004, author = {Lawlor, D. A. and Davey Smith, G. and Kundu, D. and Bruckdorfer, K. R. and Ebrahim, S.},
  title = {Those confounded vitamins: what can we learn from the differences between observational versus randomised trial evidence?},
  journal = {Lancet}, year = 2004, volume = 363, pages = {1724--1727}}
@article{vandenbroucke2004, author = {Vandenbroucke, J. P.},
  title = {When are observational studies as credible as randomised trials?},
  journal = {Lancet}, year = 2004, volume = 363, pages = {1728--1731}}
@article{michiels2005, author = {Michiels, S. and Koscielny, S. and Hill, C.},
  title = {Prediction of cancer outcome with microarrays: a multiple random validation strategy},
  journal = {Lancet}, year = 2005, volume = 365, pages = {488--492}}
@article{ioannidis2001b, author = {Ioannidis, J. P. A. and Ntzani, E. E. and Trikalinos, T. A. and Contopoulos-Ioannidis, D. G.},
  title = {Replication validity of genetic association studies},
  journal = {Nat Genet}, year = 2001, volume = 29, pages = {306--309}}
@article{colhoun2003, author = {Colhoun, H. M. and McKeigue, P. M. and Davey Smith, G.},
  title = {Problems of reporting genetic associations with complex outcomes},
  journal = {Lancet}, year = 2003, volume = 361, pages = {865--872}}
@article{ioannidis2003, author = {Ioannidis, J. P.},
  title = {Genetic associations: false or true?},
  journal = {Trends Mol Med}, year = 2003, volume = 9, pages = {135--138}}
@article{ioannidis2005a, author = {Ioannidis, J. P. A.},
  title = {Microarrays and molecular research: noise discovery?},
  journal = {Lancet}, year = 2005, volume = 365, pages = {454--455}}
@article{sterne2001, author = {Sterne, J. A. and Davey Smith, G.},
  title = {Sifting the evidence---what's wrong with significance tests?},
  journal = {BMJ}, year = 2001, volume = 322, pages = {226--231}}
@article{wacholder2004, author = {Wacholder, S. and Chanock, S. and Garcia-Closas, M. and El Ghormli, L. and Rothman, N.},
  title = {Assessing the probability that a positive report is false: an approach for molecular epidemiology studies},
  journal = {J Natl Cancer Inst}, year = 2004, volume = 96, pages = {434--442}}
@article{risch2000, author = {Risch, N. J.},
  title = {Searching for genetic determinants in the new millennium},
  journal = {Nature}, year = 2000, volume = 405, pages = {847--856}}
@article{yusuf1984, author = {Yusuf, S. and Collins, R. and Peto, R.},
  title = {Why do we need some large, simple randomized trials?},
  journal = {Stat Med}, year = 1984, volume = 3, pages = {409--422}}
@article{altman2000, author = {Altman, D. G. and Royston, P.},
  title = {What do we mean by validating a prognostic model?},
  journal = {Stat Med}, year = 2000, volume = 19, pages = {453--473}}
@article{taubes1995, author = {Taubes, G.},
  title = {Epidemiology faces its limits},
  journal = {Science}, year = 1995, volume = 269, pages = {164--169}}
@article{golub1999, author = {Golub, T. R. and Slonim, D. K. and Tamayo, P. and Huard, C. and Gaasenbeek, M. and others},
  title = {Molecular classification of cancer: class discovery and class prediction by gene expression monitoring},
  journal = {Science}, year = 1999, volume = 286, pages = {531--537}}
@article{moher2001, author = {Moher, D. and Schulz, K. F. and Altman, D. G.},
  title = {The {CONSORT} statement: revised recommendations for improving the quality of reports of parallel-group randomised trials},
  journal = {Lancet}, year = 2001, volume = 357, pages = {1191--1194}}
@article{ioannidis2004, author = {Ioannidis, J. P. and Evans, S. J. and Gøtzsche, P. C. and O'Neill, R. T. and Altman, D. G. and others},
  title = {Better reporting of harms in randomized trials: an extension of the {CONSORT} statement},
  journal = {Ann Intern Med}, year = 2004, volume = 141, pages = {781--788}}
@article{ich1999, author = {{International Conference on Harmonisation E9 Expert Working Group}},
  title = {{{ICH}} {{Harmonised Tripartite Guideline}}. Statistical principles for clinical trials},
  journal = {Stat Med}, year = 1999, volume = 18, pages = {1905--1942}}
@article{moher1999, author = {Moher, D. and Cook, D. J. and Eastwood, S. and Olkin, I. and Rennie, D. and others},
  title = {Improving the quality of reports of meta-analyses of randomised controlled trials: the {QUOROM} statement. {Quality of Reporting of Meta-analyses}},
  journal = {Lancet}, year = 1999, volume = 354, pages = {1896--1900}}
@article{stroup2000, author = {Stroup, D. F. and Berlin, J. A. and Morton, S. C. and Olkin, I. and Williamson, G. D. and others},
  title = {Meta-analysis of observational studies in epidemiology: a proposal for reporting. {Meta-analysis Of Observational Studies in Epidemiology} ({MOOSE}) group},
  journal = {JAMA}, year = 2000, volume = 283, pages = {2008--2012}}
@article{chan2004, author = {Chan, A. W. and Hróbjartsson, A. and Haahr, M. T. and Gøtzsche, P. C. and Altman, D. G.},
  title = {Empirical evidence for selective reporting of outcomes in randomized trials: comparison of protocols to published articles},
  journal = {JAMA}, year = 2004, volume = 291, pages = {2457--2465}}
@article{krimsky1998, author = {Krimsky, S. and Rothenberg, L. S. and Stott, P. and Kyle, G.},
  title = {Scientific journals and their authors' financial interests: a pilot study},
  journal = {Psychother Psychosom}, year = 1998, volume = 67, pages = {194--201}}
@article{papanikolaou2001, author = {Papanikolaou, G. N. and Baltogianni, M. S. and Contopoulos-Ioannidis, D. G. and Haidich, A. B. and Giannakakis, I. A. and others},
  title = {Reporting of conflicts of interest in guidelines of preventive and therapeutic interventions},
  journal = {BMC Med Res Methodol}, year = 2001, volume = 1, pages = {3}}
@article{antman1992, author = {Antman, E. M. and Lau, J. and Kupelnick, B. and Mosteller, F. and Chalmers, T. C.},
  title = {A comparison of results of meta-analyses of randomized control trials and recommendations of clinical experts. {Treatments for myocardial infarction}},
  journal = {JAMA}, year = 1992, volume = 268, pages = {240--248}}
@article{ioannidis2005b, author = {Ioannidis, J. P. and Trikalinos, T. A.},
  title = {Early extreme contradictory estimates may appear in published research: the {Proteus} phenomenon in molecular genetics research and randomized trials},
  journal = {J Clin Epidemiol}, year = 2005, volume = 58, pages = {543--549}}
@article{ntzani2003, author = {Ntzani, E. E. and Ioannidis, J. P.},
  title = {Predictive ability of {DNA} microarrays for cancer outcomes and correlates: an empirical assessment},
  journal = {Lancet}, year = 2003, volume = 362, pages = {1439--1444}}
@article{ransohoff2004, author = {Ransohoff, D. F.},
  title = {Rules of evidence for cancer molecular-marker discovery and validation},
  journal = {Nat Rev Cancer}, year = 2004, volume = 4, pages = {309--314}}
@article{lindley1957, author = {Lindley, D. V.}, title = {A statistical paradox},
  journal = {Biometrika}, year = 1957, volume = 44, pages = {187--192}}
@article{bartlett1957, author = {Bartlett, M. S.}, title = {A comment on {D. V. Lindley's} statistical paradox},
  journal = {Biometrika}, year = 1957, volume = 44, pages = {533--534}}
@article{senn2001, author = {Senn, S. J.},
  title = {Two cheers for {P}-values?},
  journal = {J Epidemiol Biostat}, year = 2001, volume = 6, pages = {193--204}}
@article{deangelis2004, author = {De Angelis, C. and Drazen, J. M. and Frizelle, F. A. and Haug, C. and Hoey, J. and others},
  title = {Clinical trial registration: a statement from the {International Committee of Medical Journal Editors}},
  journal = {N Engl J Med}, year = 2004, volume = 351, pages = {1250--1251}}
@article{ioannidis2005c, author = {Ioannidis, J. P. A.},
  title = {Contradicted and initially stronger effects in highly cited clinical research},
  journal = {JAMA}, year = 2005, volume = 294, pages = {218--228}}
@article{hsueh2003, author = {Hsueh, H. M. and Chen, J. J. and Kodell, R. L.},
  title = {Comparison of methods for estimating the number of true null hypotheses in multiplicity testing},
  journal = {J Biopharm Stat}, year = 2003, volume = 13, pages = {675--689}}
:::
`, // the references as BibTeX, shared by both editions
].join('\n\n');
// Captions, notes and the tables as tab-separated text with TeX in the cells, one block each.
const blocks = String.raw`id: tbl-truth
caption: Research Findings and True Relationships
Research finding	True relationship		
	Yes	No	Total
Yes	$c(1-\beta)R/(R+1)$	$c\alpha/(R+1)$	$c(R+\alpha-\beta R)/(R+1)$
No	$c\beta R/(R+1)$	$c(1-\alpha)/(R+1)$	$c(1-\alpha+\beta R)/(R+1)$
Total	$cR/(R+1)$	$c/(R+1)$	$c$

id: tbl-bias
caption: Research Findings and True Relationships in the Presence of Bias
Research finding	True relationship		
	Yes	No	Total
Yes	$(c[1-\beta]R+uc\beta R)/(R+1)$	$c\alpha+uc(1-\alpha)/(R+1)$	$c(R+\alpha-\beta R+u-u\alpha+u\beta R)/(R+1)$
No	$(1-u)c\beta R/(R+1)$	$(1-u)c(1-\alpha)/(R+1)$	$c(1-u)(1-\alpha+\beta R)/(R+1)$
Total	$cR/(R+1)$	$c/(R+1)$	$c$

id: tbl-teams
caption: Research Findings and True Relationships in the Presence of Multiple Studies
Research finding	True relationship		
	Yes	No	Total
Yes	$cR(1-\beta^n)/(R+1)$	$c(1-[1-\alpha]^n)/(R+1)$	$c(R+1-[1-\alpha]^n-R\beta^n)/(R+1)$
No	$cR\beta^n/(R+1)$	$c(1-\alpha)^n/(R+1)$	$c([1-\alpha]^n+R\beta^n)/(R+1)$
Total	$cR/(R+1)$	$c/(R+1)$	$c$

id: tbl-ppv
caption: PPV of Research Findings for Various Combinations of Power ($1-\beta$), Ratio of True to Not-True Relationships ($R$), and Bias ($u$)
note: The estimated PPVs (positive predictive values) are derived assuming $\alpha = 0.05$ for a single study; here they are computed from Eq. (2).\\RCT, randomized controlled trial.
$1-\beta$	$R$	$u$	Practical example	PPV
0.80	1:1	0.10	Adequately powered RCT with little bias and 1:1 pre-study odds	
0.95	2:1	0.30	Confirmatory meta-analysis of good-quality RCTs	
0.80	1:3	0.40	Meta-analysis of small inconclusive studies	
0.20	1:5	0.20	Underpowered, but well-performed phase I/II RCT	
0.20	1:5	0.80	Underpowered, poorly performed phase I/II RCT	
0.80	1:10	0.30	Adequately powered exploratory epidemiological study	
0.20	1:10	0.30	Underpowered exploratory epidemiological study	
0.20	1:1,000	0.80	Discovery-oriented exploratory research with massive testing	
0.20	1:1,000	0.20	As in previous example, but with more limited bias (more standardized)

id: fig-bias
caption: PPV (Probability That a Research Finding Is True) as a Function of the Pre-Study Odds for Various Levels of Bias, $u$
note: Panels correspond to power of 0.80, 0.50, and 0.20. Drawn in code from Eq. (2) for the values of $u$ in the 2005 legend, with no bias ($u = 0$) dashed; the curves printed in 2005 match $u = 0$, 0.05, 0.20 and 0.80.
alt: Three panels of rising curves: PPV grows with the pre-study odds and falls as bias grows.

id: fig-teams
caption: PPV (Probability That a Research Finding Is True) as a Function of the Pre-Study Odds for Various Numbers of Conducted Studies, $n$
note: Panels correspond to power of 0.80, 0.50, and 0.20. Drawn in code from Eq. (3).
alt: Three panels of rising curves: PPV falls as more teams test the same question.
`;

// #region tables: TSV in, a merged header, and the $…$ cells set as formulas by the engine
// A cell, a caption or a note sets $…$ as the text does (postext ≥ 1.19): MathJax paths at
// the cell's 8 pt, on the baseline of its line and aligned with the cell, vector in the PDF.
// The faces' latin files have no α or β (gotcha: latin-subset); the formulas need none.
const [CELL_PT, PAD] = [8, 1]; // cell type (pt), cell padding (mm)
function tableModel(tsv, widths, headerRows, merges = [], right = []) {
  let model = { ...parseTSV(tsv, { headerRows }), columnWidths: widths };
  model.rows.forEach((_, row) => right.forEach((c) => {
    model = setAlignment(model, { row, col: c }, 'right'); // the numbers, the PPV and their heads
  }));
  for (const range of merges) model = mergeCells(model, range); // gotcha: merged-cells-hiddenby
  return model;
}
// "Research finding" over both header rows, "True relationship" over Yes, No and Total.
const twoByTwo = (tsv) => tableModel(tsv, [1.1, 1.6, 1.6, 2.5], 2, [
  { start: { row: 0, col: 0 }, end: { row: 1, col: 0 } },
  { start: { row: 0, col: 1 }, end: { row: 0, col: 3 } }]);
// Table 4's last column is computed from Eq. (2), α = 0.05: two significant figures, as in 2005.
const ppv = (power, R, u, alpha = 0.05) => (power * R + u * (1 - power) * R)
  / (R + alpha - (1 - power) * R + u - u * alpha + u * (1 - power) * R);
const read = (s) => Number(s.replace(',', '.'));
const odds = (s) => s.split(':').map((x) => Number(x.replace(/\D/g, ''))).reduce((a, b) => a / b);
const ppvRows = (tsv) => tsv.split('\n').map((line, r) => {
  const [power, R, u, example, head] = line.split('\t');
  const p = ppv(read(power), odds(R), read(u)); // 0.0010: as many decimals as 2 figures need
  return [power, R, u, example, r ? number(p, 1 - Math.floor(Math.log10(p))) : head].join('\t');
}).join('\n');
// #endregion

// #region resources: each block of the slot is a table or a figure, placed where it is cited
const parsed = blocks.trim().split(/\n\s*\n/).map((block) => {
  const fields = {};
  const tsv = block.split('\n').filter((line) => {
    const m = /^(id|caption|note|alt): (.*)$/.exec(line);
    if (m) fields[m[1]] = m[2];
    return !m;
  }).join('\n');
  return { ...fields, tsv };
});
const tables = parsed.filter((b) => b.tsv).map(({ id, caption, note, tsv }) => {
  const model = id === 'tbl-ppv'
    ? tableModel(ppvRows(tsv), [0.8, 0.95, 0.75, 3.8, 1.05], 1, [], [0, 1, 2, 4])
    : twoByTwo(tsv);
  return { id, typeId: 'table', kind: 'table', createdAt: 0, updatedAt: 0, caption, note,
    table: { model }, placement: { position: 'auto', ...(id !== 'tbl-ppv' && { span: 'page' }) } };
});
const figures = parsed.filter((b) => !b.tsv).map(({ id, caption, note, alt }) => ({ id,
  typeId: 'figure', kind: 'svg', createdAt: 0, updatedAt: 0, caption, note, altText: alt,
  placement: { position: 'auto', span: 'page' },
  svg: { fileId: `${id}.svg`, width: FIG_W * 10, height: FIG_H * 10 } }));
const resources = [...tables, ...figures,
  { id: 'band-art', typeId: 'figure', kind: 'svg', createdAt: 0, updatedAt: 0,
    svg: { fileId: 'band-art.svg', width: 1400, height: BAND * 10 } }];
const tableStyle = { rules: 'horizontal', borderColor: col('rule'), borderWidth: pt(0.5),
  headerBackground: col('navy'), headerColor: col('paper'), headerBold: true,
  headerFontFamily: SANS, headerFontSize: pt(CELL_PT), bodyFontFamily: SANS,
  bodyFontSize: pt(CELL_PT), bodyColor: col('ink'), cellPadding: mm(PAD) };
const captionStyle = { fontFamily: SANS, fontSize: pt(8.2), color: col('ink'), labelBold: true,
  labelColor: col('accent'), gap: mm(2), note: { fontSize: pt(7), color: col('muted') } };
// #endregion

// #region art: Figures 1 and 2 drawn from Eqs. (2) and (3), the band's curves from Eq. (2)
const R2 = (x) => Math.round(x * 100) / 100;
// An SVG drawn as a picture cannot use the page's web fonts (gotcha: svg-no-webfonts): the
// figures carry the label face inline under its own name, which the PDF asks the provider for.
async function inlineFace(family, weight) {
  const id = family.toLowerCase().replace(/\s+/g, '-');
  const url = `https://cdn.jsdelivr.net/npm/@fontsource/${id}@5/files/${id}-latin-${weight}`
    + '-normal.woff2';
  const bytes = new Uint8Array(await (await fetch(url)).arrayBuffer());
  let bin = '';
  for (const b of bytes) bin += String.fromCharCode(b);
  return `@font-face{font-family:'${family}';font-weight:${weight};`
    + `src:url(data:font/woff2;base64,${btoa(bin)}) format('woff2')}`;
}
const mix = (f) => `#${[1, 3, 5].map((i) => Math.round(parseInt(palette.navy.slice(i, i + 2), 16)
  * (1 - f) + parseInt(palette.accent.slice(i, i + 2), 16) * f).toString(16).padStart(2, '0'))
  .join('')}`; // navy for the first curve, vermilion for the last
const label = (x, y, s, size, extra = '', fill = palette.muted) => `<text x="${R2(x)}" `
  + `y="${R2(y)}" font-size="${size}" font-family="${SANS}" fill="${fill}" ${extra}>${s}</text>`;
const line = (points, stroke, width, extra = '') => `<path d="M${points.map(([x, y]) =>
  `${R2(x)} ${R2(y)}`).join('L')}" fill="none" stroke="${stroke}" stroke-width="${width}" `
  + `${extra}/>`;
// Three panels, power 0.80, 0.50 and 0.20; PPV in % against R from 0 to 1, one curve per value.
function panels(face, curve, values, name, digits, dashed) {
  const [pw, top, plotH, left] = [52, 12, 34, 8]; // panel width, plot top and height, y labels
  let out = `<style>${face}</style>`;
  [0.8, 0.5, 0.2].forEach((power, p) => {
    const x0 = p * (pw + (FIG_W - 3 * pw) / 2) + left;
    const X = (R) => x0 + R * (pw - left - 2);
    const Y = (v) => top + plotH * (1 - v);
    const trace = (f) => Array.from({ length: 101 }, (_, k) => [X(k / 100), Y(f(k / 100))]);
    out += label(x0 - left, 4, `${'ABC'[p]}  <tspan font-weight="400">${t({ en: 'Power',
      es: 'Potencia' })} ${number(power)}</tspan>`, 3.4, '', palette.navy);
    if (!p) out += label(x0 - left, top - 4, t({ en: 'PPV (%)', es: 'VPP (%)' }), 2.8);
    for (const v of [0, 0.2, 0.4, 0.6, 0.8, 1]) {
      out += line([[X(0), Y(v)], [X(1), Y(v)]], palette.rule, v ? 0.15 : 0.3)
        + label(X(0) - 1.4, Y(v) + 1, v * 100, 2.6, 'text-anchor="end"')
        + label(X(v), Y(0) + 3.6, number(v, v % 1 ? 1 : 0), 2.6, 'text-anchor="middle"');
    }
    if (dashed) out += line(trace((R) => dashed(power, R)), palette.muted, 0.35,
      'stroke-dasharray="1 0.8"');
    values.forEach((value, i) => {
      out += line(trace((R) => curve(power, R, value)), mix(i / (values.length - 1)), 0.6);
    });
    out += label(X(0.5), Y(0) + 8, `${t({ en: 'Pre-study odds', es: 'Razón previa' })}, `
      + '<tspan font-style="italic">R</tspan>', 2.9, 'text-anchor="middle"');
  });
  const key = values.map((value, i) => line([[48 + i * 24, 57.8], [54 + i * 24, 57.8]],
    mix(i / (values.length - 1)), 0.8) + label(56 + i * 24, 58.8, `<tspan font-style="italic">`
    + `${name}</tspan> = ${number(value, digits)}`, 2.9)).join('');
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${FIG_W * 10}" height="${FIG_H * 10}" `
    + `viewBox="0 0 ${FIG_W} ${FIG_H}">${out}${key}</svg>`;
}
// Eq. (3): n independent studies of equal power, no bias.
const teams = (power, R, n, alpha = 0.05) => (R * (1 - (1 - power) ** n))
  / (R + 1 - (1 - alpha) ** n - R * (1 - power) ** n);
const bandArt = () => `<svg xmlns="http://www.w3.org/2000/svg" width="1400" height="${BAND * 10}" `
  + `viewBox="0 0 140 ${BAND}">${Array.from({ length: 10 }, (_, i) => line(Array.from(
    { length: 81 },
    (_, k) => [10 + k * 1.6, 92 - 76 * ppv(0.8, k / 80, i / 10)]), palette.mist, 0.5,
  `stroke-opacity="${R2(0.5 - i * 0.04)}"`)).join('')}</svg>`;
// #endregion

// ─── 3 · Fonts ──────────────────────────────────────────────────────────────
const FONTS = { // every face the pages paint, loaded before the first build (gotcha: fonts-first)
  Gelasio: ['400', '400i', '600', '700', '700i'],
  'Sofia Sans Semi Condensed': ['400', '400i', '600', '700', '700i', '800'],
};

// ─── 4 · Build & show ───────────────────────────────────────────────────────
await loadFonts(FONTS, markdown + blocks);
const face = await inlineFace(SANS, 400); // one face: the figures set no bold
await loadSvg('fig-bias.svg', panels(face, ppv, [0.05, 0.2, 0.5, 0.8], 'u', 2,
  (power, R) => ppv(power, R, 0)));
await loadSvg('fig-teams.svg', panels(face, teams, [1, 5, 10, 50], 'n', 0));
await loadSvg('band-art.svg', bandArt());
const doc = await buildWithFonts(() => buildDocument({ markdown, resources }, config()), markdown);
showPages(doc, { title: t({ en: 'A metascience essay in English and Spanish',
  es: 'Un ensayo de metaciencia en inglés y en español' }) });
offerPdf(() => renderToPdf(doc, { fontProvider: fontsourceProvider, resourceBytes: imageBytes }),
  `${RECIPE}.pdf`); // text in the Fontsource faces; formulas and figures as vector paths

// ─── Kit ── helpers shared by every Cookbook recipe · postext.dev/cookbook ─────

// ─── Kit · core v1 ── the same in every recipe · postext.dev/cookbook
function mm(value) { return { value, unit: 'mm' }; }
function pt(value) { return { value, unit: 'pt' }; }
function em(value) { return { value, unit: 'em' }; }
/** The sample language's string: t({ en: 'Figure', es: 'Figura' }). */
function t(strings) { return strings[LANG] ?? Object.values(strings)[0]; }
/** A file in this recipe's assets folder, served from the Postext repo by jsDelivr. */
function asset(file) { return `https://cdn.jsdelivr.net/gh/drnachio/postext@main/cookbook/${RECIPE}/assets/${file}`; }

// ─── Kit · fonts v2 ── the same in every recipe · postext.dev/cookbook
// Postext measures with the loaded faces and caches the widths: load every face
// before the first build, from Fontsource, the files the PDF embeds too.

/** faces = { 'Family Name': ['400', '400i', '700'] }. `text` is the sample:
 *  č ł † α χ also load latin-ext and greek files (kitSubsetsFor). With
 *  `optional`, a face Fontsource does not ship is skipped instead of failing.
 *  Resolves to the number of faces added. */
async function loadFonts(faces, text = '', { optional = false } = {}) {
  kitStatus('Loading fonts…');
  const ranges = {
    latin: 'U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,'
      + 'U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD',
    'latin-ext': 'U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,'
      + 'U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF',
    greek: 'U+0370-03FF',
  };
  const jobs = [];
  let added = 0;
  for (const [family, specs] of Object.entries(faces)) {
    const id = fontsourceId(family);
    const todo = [...new Set(specs)].map((spec) => [parseInt(spec, 10), spec.endsWith('i') ? 'italic' : 'normal'])
      .filter(([weight, style]) => !hasFace(family, weight, style)); // before any await
    const meta = optional || /[^\0-ÿ]/u.test(text) ? await fontsourceMeta(family) : null;
    const subsets = ['latin', ...kitSubsetsFor(text, meta)];
    for (const [weight, style] of todo) {
      if (optional && !(meta?.weights.includes(weight) && meta.styles.includes(style))) continue;
      for (const subset of subsets) {
        const url = `https://cdn.jsdelivr.net/npm/@fontsource/${id}@5/files/${id}-${subset}-${weight}-${style}.woff2`;
        const face = new FontFace(family, `url(${url}) format('woff2')`,
          { weight: String(weight), style, unicodeRange: ranges[subset] });
        jobs.push(face.load().then((ready) => { document.fonts.add(ready); added++; }, () => {
          if (subset === 'latin' && !optional) throw new Error(`Fontsource has no ${family} ${weight} ${style}`);
        }));
      }
    }
  }
  await Promise.all(jobs).catch((error) => { kitFail(error); throw error; });
  return added;
}

/** Runs `build` and loads any face the pages use that FONTS missed (a regular
 *  one with a warning), then clears the measurement cache and builds again. */
async function buildWithFonts(build, text = '') {
  const tried = new Set();
  for (let round = 0; round < 3; round++) {
    kitStatus('Laying out…');
    await new Promise(requestAnimationFrame);          // let the status paint first
    const result = await Promise.resolve().then(build).catch((error) => { kitFail(error); throw error; });
    const wanted = { base: {}, variants: {} };
    for (const { font, base } of [result].flat().flatMap(fontStringsOf)) {
      const { family, weight, style } = parseFont(font);
      const key = `${family}|${weight}|${style}`;
      if (tried.has(key) || hasFace(family, weight, style)) continue;
      tried.add(key);
      (wanted[base ? 'base' : 'variants'][family] ??= []).push(`${weight}${style === 'italic' ? 'i' : ''}`);
    }
    if (Object.keys(wanted.base).length) {
      console.warn(`[cookbook] FONTS does not list ${JSON.stringify(wanted.base)}: loading them.`);
    }
    const added = await loadFonts(wanted.base, text) + await loadFonts(wanted.variants, text, { optional: true });
    if (added === 0) return result;
    clearMeasurementCache();
  }
  throw new Error('The fonts did not settle after three builds.');
}

/** Every font string of the layout; `base` marks a block's own face. */
function fontStringsOf(doc) {
  const found = new Map();
  const walk = (node) => {
    if (!node || typeof node !== 'object') return;
    if (Array.isArray(node)) { node.forEach(walk); return; }
    for (const [key, value] of Object.entries(node)) {
      if (typeof value === 'string' && /fontString$/i.test(key)) {
        found.set(value, found.get(value) || key === 'fontString');
      } else if (value && typeof value === 'object') walk(value);
    }
  };
  walk(doc.pages);
  walk(doc.blocks);
  return [...found].map(([font, base]) => ({ font, base }));
}

/** '700 37.5px Open Sans' / 'italic 400 13px "Source Serif 4"' → { family, weight, style }.
 *  A string with no weight ('95.8px Young Serif', from a design text) is 400. */
function parseFont(font) {
  const m = /^(?:(italic|oblique)\s+)?(?:small-caps\s+)?(?:(\d+|bold|normal)\s+)?[\d.]+px\s+(.+)$/.exec(font.trim());
  if (!m) throw new Error(`Unexpected font string: ${font}`);
  const weight = m[2] === 'bold' ? 700 : !m[2] || m[2] === 'normal' ? 400 : Number(m[2]);
  return { family: m[3].replace(/^["']|["']$/g, ''), weight, style: m[1] ? 'italic' : 'normal' };
}

/** A loaded FontFace covers this family, weight and style (fonts.check() would
 *  also say yes for families nobody declared). */
function hasFace(family, weight, style) {
  for (const face of document.fonts) {
    if (face.status !== 'loaded' || face.style !== style) continue;
    if (face.family.replace(/^["']|["']$/g, '') !== family) continue;
    const [low, high = low] = face.weight.split(' ').map(Number);
    if (weight >= low && weight <= high) return true;
  }
  return false;
}

/** The files beyond latin `text` needs that `meta`'s family ships. */
function kitSubsetsFor(text, meta) {
  return [[/[Ā-˿ᴀ-ᶿḀ-ỿ†ℓⱠ-Ɀ꜠-ꟿ]/u, 'latin-ext'], [/[Ͱ-Ͽ]/u, 'greek']]
    .filter(([re, x]) => re.test(text) && meta?.subsets?.includes(x)).map(([, x]) => x);
}

/** Fontsource's id for a family: 'Source Serif 4' → 'source-serif-4'. */
function fontsourceId(family) { return family.toLowerCase().replace(/\s+/g, '-'); }

/** The family's Fontsource metadata (weights, styles, subsets), or null. */
function fontsourceMeta(family) {
  fontsourceMeta.cache ??= new Map();
  const id = fontsourceId(family);
  if (!fontsourceMeta.cache.has(id)) {
    fontsourceMeta.cache.set(id, fetch(`https://api.fontsource.org/v1/fonts/${id}`)
      .then((res) => (res.ok ? res.json() : null), () => null));
  }
  return fontsourceMeta.cache.get(id);
}

// ─── Kit · viewer v1 ── the same in every recipe · postext.dev/cookbook
/** The pages as spreads on a dark desk, page 1 alone, then verso | recto,
 *  each painted when it scrolls near. */
function showPages(docs, { title, width = 460 } = {}) {
  const root = viewer(title);
  const pages = [docs].flat().flatMap((doc) =>
    doc.pages.map((page) => ({ doc, page, n: (doc.pageIndexOffset ?? 0) + page.index })));
  const spreads = [];
  let verso = null;
  for (const p of pages) {
    if (p.n % 2 === 1) { if (verso) spreads.push([verso, null]); verso = p; }
    else { spreads.push([verso, p]); verso = null; }
  }
  if (verso) spreads.push([verso, null]);
  const density = Math.min(window.devicePixelRatio || 1, 2);
  showPages.painter?.disconnect();
  const painter = new IntersectionObserver((entries) => {
    for (const { isIntersecting, target } of entries) {
      if (!isIntersecting) continue;
      painter.unobserve(target);
      const { doc, page } = target.postext;
      renderPageToCanvas(page, doc, target, { scale: (width * density) / page.width });
    }
  }, { rootMargin: '800px' });
  showPages.painter = painter;
  root.replaceChildren(...spreads.map((pair) => {
    const spread = document.createElement('div');
    spread.className = 'pt-spread';
    for (const p of pair) {
      const figure = document.createElement('figure');
      if (p) {
        const label = p.page.pageLabel || String(p.n + 1);
        const canvas = document.createElement('canvas');
        canvas.postext = p;
        canvas.style.aspectRatio = `${p.page.width} / ${p.page.height}`;
        canvas.setAttribute('role', 'img');
        canvas.setAttribute('aria-label', `Page ${label}`);
        const folio = document.createElement('figcaption');
        folio.textContent = label;
        figure.append(canvas, folio);
        painter.observe(canvas);
      } else figure.className = 'pt-blank';
      spread.append(figure);
    }
    return spread;
  }));
  kitStatus(`${pages.length} ${pages.length === 1 ? 'page' : 'pages'}`);
  document.documentElement.dataset.postext = 'ready';
  return pages.length;
}

/** The desk, the bar and the error reporting, created once. */
function viewer(title) {
  if (!document.getElementById('pt-kit')) {
    document.head.insertAdjacentHTML('beforeend', `<style id="pt-kit">
      :root { color-scheme: dark; }
      body { margin: 0; background: #0e1014; color: #b9bcc4; font: 13px/1.45 system-ui, sans-serif; }
      #pt-bar { position: sticky; top: 0; z-index: 1; display: flex; flex-wrap: wrap; align-items: center;
        gap: 6px 16px; padding: 10px 16px; background: rgb(14 16 20 / .92); backdrop-filter: blur(6px);
        border-bottom: 1px solid #23262d; }
      #pt-bar strong { color: #f4f1ea; font-weight: 600; }
      #pt-actions { display: flex; gap: 12px; margin-left: auto; }
      #pt-actions a, #pt-actions button { color: #d8a21a; font: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
      #pages { display: grid; justify-items: center; gap: 48px; padding: 32px 16px 72px; }
      .pt-spread { display: flex; }
      .pt-spread figure { margin: 0; width: min(460px, 44vw); }
      .pt-spread canvas { display: block; width: 100%; background: #fff;
        box-shadow: 0 1px 2px rgb(0 0 0 / .5), 0 22px 44px -16px rgb(0 0 0 / .8); }
      .pt-spread figure:first-child canvas { box-shadow: inset -14px 0 14px -14px rgb(0 0 0 / .18), 0 1px 2px rgb(0 0 0 / .5), 0 22px 44px -16px rgb(0 0 0 / .8); }
      .pt-spread figcaption { margin-top: 10px; text-align: center; font: 600 10px/1 system-ui, sans-serif;
        letter-spacing: .18em; text-transform: uppercase; color: #6c7079; }
      .pt-blank { visibility: hidden; }
      @media (max-width: 760px) {
        .pt-spread { flex-direction: column; gap: 32px; }
        .pt-spread figure { width: min(460px, 92vw); }
        .pt-blank { display: none; }
      }
    </style>`);
    document.body.insertAdjacentHTML('afterbegin',
      '<header id="pt-bar"><strong id="pt-title"></strong><span id="pt-status" role="status"></span><span id="pt-actions"></span></header>');
    document.getElementById('pt-title').textContent = document.title || 'Postext';
    addEventListener('error', (event) => kitFail(event.error ?? event.message));
    addEventListener('unhandledrejection', (event) => kitFail(event.reason));
  }
  if (title) document.getElementById('pt-title').textContent = title;
  return document.getElementById('pages')
    ?? document.body.appendChild(Object.assign(document.createElement('main'), { id: 'pages' }));
}

function kitStatus(text) {
  viewer();
  document.getElementById('pt-status').textContent = text;
}

function kitFail(error) {
  document.documentElement.dataset.postext = 'error';
  kitStatus(`Error: ${error?.message ?? error}`);
}

// ─── Kit · pdf v2 ── the same in every recipe that exports a PDF
/** The Fontsource files the screen used, as TrueType: the nearest weight the
 *  family ships, upright if it has no italic; latin, then what the face's
 *  letters need (kitSubsetsFor). */
async function fontsourceProvider(family, weight, style, request) {
  const id = fontsourceId(family);
  const meta = await fontsourceMeta(family);
  const weights = meta?.weights?.length ? meta.weights : [400, 700];
  const w = weights.reduce((a, b) => (Math.abs(b - weight) < Math.abs(a - weight) ? b : a));
  const s = style === 'italic' && meta && !meta.styles.includes('italic') ? 'normal' : style;
  const text = String.fromCodePoint(...(request?.codePoints ?? []));
  const more = kitSubsetsFor(text, meta);
  const files = await Promise.all(['latin', ...more].map(async (subset) => {
    const res = await fetch(`https://cdn.jsdelivr.net/npm/@fontsource/${id}@5/files/${id}-${subset}-${w}-${s}.woff2`);
    if (!res.ok) throw new Error(`Fontsource has no ${family} ${w} ${s} ${subset}`);
    return decompressWoff2(new Uint8Array(await res.arrayBuffer()));
  }));
  return files.length === 1 ? files[0] : files;
}

/** A "Build the PDF" button; then "Open the PDF" (a new tab: CodePen's frame
 *  shows no PDFs) and a download link. */
function offerPdf(makePdf, filename) {
  viewer();
  const button = Object.assign(document.createElement('button'), { type: 'button', textContent: 'Build the PDF' });
  button.dataset.postextPdf = filename;
  button.addEventListener('click', async () => {
    button.disabled = true;
    button.textContent = 'Building the PDF…';
    try {
      const bytes = await makePdf();
      const url = URL.createObjectURL(new Blob([bytes], { type: 'application/pdf' }));
      const size = `${Math.max(1, Math.round(bytes.length / 1024))} KB`;
      button.replaceWith(
        Object.assign(document.createElement('a'), { href: url, target: '_blank', rel: 'noopener', textContent: 'Open the PDF ↗' }),
        Object.assign(document.createElement('a'), { href: url, download: filename, textContent: `Download ${filename} · ${size}` }));
    } catch (error) {
      button.disabled = false;
      button.textContent = 'Build the PDF';
      kitFail(error);
    }
  });
  document.getElementById('pt-actions').append(button);
}

// ─── Kit · images v1 ── recipes with pictures · postext.dev/cookbook
/** Registers a photo or PNG for the canvas and keeps its bytes for the PDF.
 *  fetch → ImageBitmap never taints the canvas (a plain cross-origin <img> would). */
async function loadImage(fileId, url) {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`Image not found (${res.status}): ${url}`);
  const bytes = new Uint8Array(await res.arrayBuffer());
  registerResourceImage(fileId, await createImageBitmap(new Blob([bytes])));
  (loadImage.bytes ??= new Map()).set(fileId, bytes);
}

/** Registers SVG markup (drawn in code, or fetched) as a vector image. */
async function loadSvg(fileId, svg) {
  const img = new Image();
  img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
  await img.decode();
  registerResourceImage(fileId, img);
  (loadImage.bytes ??= new Map()).set(fileId, new TextEncoder().encode(svg));
}

/** renderToPdf({ resourceBytes: imageBytes }) */
function imageBytes(fileId) { return loadImage.bytes?.get(fileId); }

/** renderToHtml({ resourceImageUrl: imageUrl }) */
function imageUrl(fileId) {
  const bytes = imageBytes(fileId);
  if (!bytes) return undefined;
  imageUrl.urls ??= new Map();
  if (!imageUrl.urls.has(fileId)) {
    const type = /\.svg$/i.test(fileId) ? 'image/svg+xml' : /\.png$/i.test(fileId) ? 'image/png' : 'image/jpeg';
    imageUrl.urls.set(fileId, URL.createObjectURL(new Blob([bytes], { type })));
  }
  return imageUrl.urls.get(fileId);
}

// ─── /Kit ───────────────────────────────────────────────────────────────────────
```

## Variações

### Elevar os números de citação

Revistas médicas costumam compor os números de Vancouver sobrescritos.

```diff
-    marker: 'brackets', collapseRanges: true,
+    marker: 'superscript', collapseRanges: true,
```

### Imprimir os DOIs

O BibTeX desta receita não guarda os DOIs, para não passar do tamanho do script. Mantenha-os no seu arquivo e imprima-os como links.

```diff
-      labelWidth: mm(6.4), doi: 'hide' } },
+      labelWidth: mm(6.4), doi: 'link' } },
```

### Compor as tabelas numa coluna

Uma tabela 2×2 com fórmulas mais curtas cabe numa coluna: retire o `span: 'page'` dela, como a Tabela 4, que não o tem. Veja [Artigo em duas colunas com equações numeradas](https://postext.dev/pt/cookbook/journal-article-with-maths.md) para uma tabela numa coluna.

## Erros comuns

- **Matemática precisa de https://esm.sh/postext?bundle e initMathEngine().** Fórmulas carregadas de https://esm.sh/postext viram caixas cinza, sem nenhum erro. Importe todos os símbolos de https://esm.sh/postext?bundle, sem nunca misturar as duas URLs, e aguarde initMathEngine() antes da primeira composição.
- **Os arquivos do Fontsource cobrem letras, não símbolos.** O kit incorpora o arquivo latin do Fontsource de cada fonte e, quando o texto usa esses caracteres, o arquivo latin-ext para č, ł, † e o arquivo greek para α, χ (em uma família que o tenha: Lora e Gelasio não têm), na tela e no PDF. Símbolos como →, ≈, ✓ e ★ não estão em nenhum desses arquivos e somem no PDF: desenhe-os, componha-os como matemática ou escolha uma fonte cujos arquivos os tenham.
- **Células mescladas precisam de hiddenBy: use mergeCells.** As células são posicionadas pela posição no array da linha, então uma célula mesclada precisa de células de preenchimento marcadas com hiddenBy onde ela se estende; omiti-las, como no HTML, desloca todas as colunas seguintes. Crie as mesclagens com mergeCells.
- **Só 8 idiomas têm hifenização, com o código exato.** A hifenização existe para en-us, es, fr, de, it, pt, ca e nl, com o código exato: 'es-ES' ou qualquer outro idioma passa sem aviso para o inglês americano.
- **Traduza Figura e Tabela com defaultResourceTypes(locale).** O locale da configuração define a hifenização, não as legendas: sem resourceTypes, os tipos embutidos dizem Figure e Table, em inglês. Passe resourceTypes: defaultResourceTypes('es') para o espanhol; para qualquer outro idioma, escreva você mesmo os nomes em resourceTypes.
- **O texto dentro de um SVG <img> não pode usar fontes web.** Um SVG é desenhado como imagem, e uma imagem não tem acesso às fontes web da página, então os rótulos dele caem em uma fonte do sistema. Converta o texto em contornos, incorpore um subconjunto @font-face no SVG ou passe os rótulos para a legenda.
- **Um $ solto abre matemática: escreva \$.** O cifrão abre matemática em linha, então um preço como $40 inicia uma fórmula. Escreva \$40.
- **Qualquer objeto headings desativa a quebra de página do H1.** Por padrão, um H1 salta para uma página ímpar (always-odd), mas passar qualquer objeto headings redefine esse padrão, então os capítulos ficam emendados e span: 'page' não faz nada. Declare de novo headings.levels[0].breakBefore: { enabled: true, parity } em toda configuração.
- **Carregue todas as fontes antes do layout.** O motor de layout mede o texto com as fontes que o navegador carregou e guarda as larguras em cache, então uma fonte que chega depois da primeira composição deixa quebras de linha erradas e um PDF que não corresponde mais à tela. Carregue antes todos os pesos e estilos e chame clearMeasurementCache() antes de recompor quando alguma chegar atrasada.
- **Coloque entre aspas cada valor do frontmatter.** O YAML lê title: 1984 como número e uma data como objeto Date, e valores que não são strings saem vazios nos placeholders e deixam o PDF sem título. Coloque cada valor entre aspas: title: "1984".

- Onde a lista de 2005 para em cinco autores e *et al.*, o BibTeX termina a lista com `and others`, e `elsevier-vancouver` imprime *et al.* no lugar (desde o postext 1.19; versões anteriores imprimiam um coautor chamado *others*).
- As páginas não coincidem entre as edições: o texto espanhol é mais longo, então a Tabela 4 cai uma página depois. A captura publica as páginas 1, 2, 3, 5, 6 e 7 das duas, para manter as imagens das páginas dentro do limite de tamanho e ainda mostrar a Tabela 4 em cada uma.

## Créditos

- Receita: Ignacio Ferro ([@drnachio](https://github.com/drnachio))
- Texto: “Why Most Published Research Findings Are False”, PLoS Medicine 2(8): e124 (2005), doi:10.1371/journal.pmed.0020124, open access under the Creative Commons Attribution License; abridged, tables and figures redrawn; Spanish translation made for the Cookbook: John P. A. Ioannidis ([fonte](https://doi.org/10.1371/journal.pmed.0020124)), CC-BY
- Tipos: Gelasio (OFL-1.1), Sofia Sans Semi Condensed (OFL-1.1)
- Código: MIT · Conteúdo de exemplo: CC-BY-4.0

## Relacionadas

- [Nº 002 · Artigo em duas colunas com equações numeradas](https://postext.dev/pt/cookbook/journal-article-with-maths.md): Um artigo de física em duas colunas com fórmulas no texto e sete equações numeradas, compostas pelo MathJax da versão ?bundle e vetoriais no PDF. · Nível 3 (Avançado) · Artigos e trabalhos acadêmicos
- [Nº 095 · Um artigo médico no estilo Vancouver](https://postext.dev/pt/cookbook/medical-article-vancouver.md): Relato de ensaio clínico em A4 e duas colunas: números de citação sobrescritos, referências numeradas no estilo Vancouver e DOIs que abrem como links no PDF. · Nível 2 (Intermediário) · Artigos e trabalhos acadêmicos
- [Nº 028 · Relatório de laboratório: fórmulas, índices e curva de pH](https://postext.dev/pt/cookbook/lab-report-formulas.md): Um relatório de química de quatro páginas A4 que marca com ~ e ^ as fórmulas e unidades do texto e deixa o TeX para a reação e as equações. · Nível 3 (Avançado) · Relatórios
