←Filipe Vieira
Nouli
Projeto pessoal · App iOSPersonal project · iOS app

Nouli

Um leitor para iPhone que converte PDF em texto corrido, no próprio aparelho, para ler no celular como um livro.An iPhone reader that converts PDFs into flowing text, on the device itself, so they can be read on a phone like a book.

PapelRole
Tudo: pesquisa, produto, design e códigoEverything: research, product, design and code
PeríodoPeriod
2026 — em andamento2026 — ongoing
Stack
Expo · React Native · TypeScript · pdf.js · SQLite
EstadoStatus
Em desenvolvimento, ainda sem usuáriosIn development, no users yet

01 · O problema01 · The problem

O PDF tem página de tamanho fixo, pensada para papel. Na tela do celular o texto fica pequeno, e ler costuma exigir zoom e rolagem para os lados.A PDF has a fixed page size, designed for paper. On a phone screen the text gets small, and reading usually takes zooming and scrolling sideways.

Os leitores mais comuns mostram o PDF como a página original. Isso funciona no computador e no tablet, mas fica difícil no celular. Quem mais lê PDF no celular costuma ser estudante com apostila, quem estuda para concurso, quem lê artigo técnico e quem baixa livro em domínio público. Uma review na Play Store descreve a situação assim: “eu queria ler pdfs como se fosse livro […] você tem que ficar arrastando a tela cada linha”.The most common readers show a PDF as the original page. That works on a computer or a tablet, but it gets hard on a phone. The people who read PDFs on a phone most are often students with course packs, people studying for civil service exams, readers of technical papers and people who download public domain books. A Play Store review describes it like this: “I wanted to read PDFs like a book […] you have to keep dragging the screen for every line”.

  • 01Refluir: extrair o texto e reconstruir parágrafos, títulos e notas numa página contínua, com fonte, tamanho e tema ajustáveis.Reflow: extract the text and rebuild paragraphs, headings and notes on a continuous page, with adjustable font, size and theme.
  • 02Tirar do fluxo do texto o cabeçalho, o rodapé, o número de página e o hífen de fim de linha.Remove running headers, footers, page numbers and end-of-line hyphens from the flow of the text.
  • 03Guardar onde a pessoa parou em cada livro, um pedido frequente nas reviews das lojas.Keep track of where the reader stopped in each book, a frequent request in store reviews.

02 · A pesquisa02 · The research

13 apps · ~1.390 reviews lidas13 apps · ~1,390 reviews read
O que já existeWhat already exists
Adobe, Modo LíquidoAdobe Liquid Mode
  • Converte nos servidores da AdobeConverts on Adobe's servers
  • Limite de 200 páginas ou 10 MB por arquivoLimited to 200 pages or 10 MB per file
  • Não converte PDF escaneadoDoesn't convert scanned PDFs
  • Pede login e oferece um assistente de IA no leitorAsks for a login and offers an AI assistant in the reader
A proposta do NouliWhat Nouli does
Nouli
  • Converte no aparelho, offlineConverts on the device, offline
  • Livro inteiro, sem limite de páginasWhole books, no page limit
  • Guarda a posição de leituraKeeps your reading position
  • O que não converte vira recorte, com o original a um toqueWhat doesn't convert becomes a crop, with the original one tap away

Antes de desenhar as telas, levantei os apps de leitura só com fontes públicas e classifiquei à mão as reviews de 1 a 4 estrelas que citam PDF. Depois testei a ideia com um painel simulado de 20 compradores. Sincronização não pesou na decisão de compra, a preocupação mais citada foi a conversão estragar tabelas e fórmulas, e 2 de 20 pagariam pelo plano pago, os dois por causa de PDF escaneado. A conclusão foi que existe espaço, mas pequeno, e que ele depende mais da qualidade da conversão do que de uma tecnologia nova.Before designing the screens, I surveyed reading apps from public sources only and hand-classified the 1 to 4 star reviews that mention PDFs. Then I tested the idea with a simulated panel of 20 buyers. Sync didn't weigh on the decision to buy, the most frequent concern was the conversion breaking tables and formulas, and 2 of 20 would pay for the paid tier, both because of scanned PDFs. The conclusion was that there is room, but not much, and that it depends more on conversion quality than on a new technology.

03 · Conversão no aparelho03 · On-device conversion

O PDF é convertido no próprio celular e não é enviado para nenhum servidor.The PDF is converted on the phone itself and isn't sent to any server.

Converter num servidor seria mais simples e daria um reconhecimento de texto melhor. Em troca, haveria custo por página, necessidade de conta e arquivos de terceiros passando por um servidor meu, com as obrigações de LGPD e o risco jurídico que isso traz. No aparelho, o uso não tem custo, funciona offline e a privacidade vale também no plano pago.Converting on a server would be simpler and would give better text recognition. In return, there would be a per-page cost, an account requirement and third-party files passing through a server of mine, with the privacy-law obligations and legal risk that brings. On the device, use has no cost, it works offline and privacy holds in the paid tier too.

A conversão roda com pdf.js e o mesmo pipeline em JavaScript do protótipo, dentro de um DOM component do Expo. O Hermes, motor de JavaScript do React Native, não atende aqui: não tem JIT, não tem ReadableStream, que o pdf.js usa para ler o texto, e não tem canvas, que os recortes e a capa precisam. A WebView do iOS tem as três coisas. Cada livro usa uma WebView, montada no início da conversão e desmontada no fim. Cancelar é desmontar, e a memória é liberada em seguida.Conversion runs on pdf.js and the same JavaScript pipeline as the prototype, inside an Expo DOM component. Hermes, React Native's JavaScript engine, doesn't fit here: it has no JIT, no ReadableStream, which pdf.js uses to read text, and no canvas, which crops and covers need. The iOS WebView has all three. Each book gets one WebView, mounted when conversion starts and torn down when it ends. Cancelling is unmounting, and the memory is released right after.

No iOS 26.5 toda conversão falhava em 0%: o WebKit só suporta for await em streams a partir do iOS 27, onde eu testava. Desde então, valido no runtime mais antigo que o app suporta, e não só no mais novo disponível.On iOS 26.5 every conversion failed at 0%: WebKit only supports for await over streams from iOS 27, where I was testing. Since then, I validate on the oldest runtime the app supports, not only on the newest one available.

04 · Como a conversão é medida04 · How conversion is measured

20 PDFs · rubrica de 8 critérios20 PDFs · 8-criteria rubric

Antes de escrever o app, montei um corpus de 20 PDFs reais e uma rubrica para avaliar a conversão.Before writing the app, I built a corpus of 20 real PDFs and a rubric to evaluate the conversion.

Romances, livros técnicos, lei, edital, apostila, artigos em duas colunas e xerox escaneado, só com material em domínio público ou licença aberta. Cada página é avaliada em 8 critérios, de ordem de leitura a notas de rodapé. Recorte nunca zera a nota; texto embaralhado sempre zera, porque é a preocupação mais citada por quem usa.Novels, technical books, a law, an exam notice, a course pack, two-column papers and scanned photocopies, all public domain or openly licensed. Each page is scored on 8 criteria, from reading order to footnotes. A crop never scores zero; scrambled text always does, because it's the concern users mention most.

822→1

páginas com cabeçalho ou rodapé vazando no meio do textopages with a header or footer leaking into the text

+49,1%+49.1%→+2,9%+2.9%

palavras a mais na Lei 8.112, por causa do texto revogado tachadoextra words in Law 8,112, caused by struck-through repealed text

2,77%2.77%→0,08%0.08%

palavras partidas por hífen de fim de linha em Dom Casmurrowords split by end-of-line hyphens in Dom Casmurro

9/20→12/20

documentos aprovados na rubrica, com o código congelado antes de pontuardocuments passing the rubric, with the code frozen before scoring

O último número tem uma ressalva. Numa segunda amostra, sorteada antes da rodada com semente fixa, o resultado cai para 10 de 20: parte do ganho veio de corrigir justamente os casos que a primeira amostra expunha. A meta para sair dessa fase é 16 de 20, e não foi atingida. Sem reconhecimento de texto, o teto é 14, porque seis PDFs do corpus não têm texto nenhum.The last number has a caveat. On a second sample, drawn before the round with a fixed seed, the result drops to 10 out of 20: part of the gain came from fixing exactly the cases the first sample exposed. The target to leave this phase is 16 out of 20, and it wasn't met. Without text recognition the ceiling is 14, because six PDFs in the corpus have no text at all.

05 · O design05 · The design

Moodboard → 4 direções → RabiscoMoodboard → 4 directions → Scribble

Do moodboard a quatro direçõesFrom moodboard to four directions

A primeira versão saiu direto do moodboard: estante de acrílico colorido, serifa de alto contraste, botão em pílula preta. Funcionava, mas ficava parecida com outros apps de leitura. Em vez de ajustar essa versão, desenhei quatro direções completas, cada uma com paleta clara e escura, tipografia, as quatro telas principais e um detalhe próprio.The first version came straight from the moodboard: a colored acrylic shelf, a high-contrast serif, a black pill button. It worked, but it looked similar to other reading apps. Instead of adjusting that version, I designed four complete directions, each with light and dark palettes, type, the four core screens and a detail of its own.

  • ARabisco: a margem do livro vira personagem. Bege, verde-limão e traço à mão.Scribble: the book's margin becomes a character. Beige, lime green and hand-drawn lines.
  • BColofão: uma editora de bolso dentro do celular, com a tipografia como capa.Colophon: a pocket publisher inside the phone, with type as the cover.
  • CFicha: cada livro é uma ficha de biblioteca, e ler é carimbar.Card: every book is a library card, and reading is stamping it.
  • DVaral: PDFs pendurados como folhetos de cordel numa feira.Clothesline: PDFs hung like cordel booklets at a street market.
Exploração da direção Rabisco: biblioteca, conversão, leitor e ajustes

A escolhida foi a A: bege quente como papel de pão, tinta quase preta e um verde-limão usado só onde há progresso (meta, destaque, botão de avançar). O leitor não tem ilustrações, para não disputar atenção com o texto.Direction A was chosen: warm beige like bakery paper, near-black ink and a lime green used only where there's progress (goal, highlight, the next button). The reader has no illustrations, so nothing competes with the text.

Livro abertoOpen book
CaféCoffee
LumináriaLamp
Lua e estrelasMoon and stars
ÓculosGlasses
BalançaScales
PlantaPlant
MapaMap

06 · O nome06 · The name

O projeto começou como “Book”, que funciona como nome de pasta mas não como marca: é a palavra genérica do produto, difícil de encontrar na loja, e os domínios estavam ocupados. Nomes de 3 ou 4 letras também. Nouli é curto, ainda não tem significado e combina com o logotipo de traço gordo. Os domínios .app e .com.br estavam livres, e nenhum app nas lojas brasileiras usava o nome.The project started as “Book”, which works as a folder name but not as a brand: it's the generic word for the product, hard to find in the store, and the domains were taken. So were three and four letter names. Nouli is short, has no meaning yet and fits the chunky line logo. The .app and .com.br domains were free, and no app in the Brazilian stores used the name.

07 · O leitor07 · The reader

Leitor do Nouli no modo Páginas, com Dom Casmurro refluído

Ler em páginasReading in pages

O livro inteiro é diagramado em colunas CSS dentro de uma WebView: uma coluna é uma página. Mudou a fonte, o tamanho ou girou o celular, tudo repagina numa passada. A posição é guardada como bloco e caractere, e não como número de página.The whole book is laid out in CSS columns inside a WebView: one column is one page. Change the font, the size or rotate the phone and it all repaginates in one pass. The position is stored as block and character, not as a page number.

Folha para escolher uma ilustração de traço como capa do livro

Uma capa para cada PDFA cover for every PDF

A capa padrão é a primeira página do PDF. Dá para trocar por outra página, uma foto, uma capa tipográfica ou uma das ilustrações de traço. Tudo é renderizado no aparelho.The default cover is the PDF's first page. You can swap it for another page, a photo, a typographic cover or one of the line illustrations. All of it is rendered on the device.

Cartão verde-limão de meta do dia concluída sobre a página do livro

Meta do diaDaily goal

O tempo de leitura vem das sessões guardadas no aparelho. Quando a meta fecha, aparece um cartão limão uma vez por dia, sem modal e sem tirar o foco do VoiceOver.Reading time comes from sessions stored on the device. When the goal is met, a lime card shows up once a day, with no modal and without taking VoiceOver focus.

08 · Detalhes de implementação08 · Implementation details

Recorte quando não converteA crop when it can't convert

Tabela, fórmula ou figura que o reflow não reconstrói entra como recorte da página, e um toque abre o PDF original. O app prefere mostrar um recorte a mostrar texto embaralhado.A table, formula or figure the reflow can't rebuild goes in as a crop of the page, and one tap opens the original PDF. The app would rather show a crop than scrambled text.

WebView sem acesso a arquivosA WebView with no file access

A WebView que converte não acessa arquivos. O código nativo entrega o PDF em pedaços de 1 MB e valida cada nome de imagem que ela devolve, já que o arquivo processado vem de fora.The converting WebView has no file access. Native code hands it the PDF in 1 MB chunks and validates every image name it sends back, since the file being processed comes from outside.

Conversão que não travaConversion that doesn't hang

O WebKit suspende os timers de uma WebView invisível, e a conversão parava na página 56. O motor passou a ceder a vez por MessageChannel, e um watchdog de 30 s mostra “Tentar de novo” se nada avançar.WebKit suspends timers in an invisible WebView, and conversion stopped on page 56. The engine now yields through MessageChannel, and a 30 s watchdog shows “Try again” if nothing moves.

Contraste medidoMeasured contrast

Seis temas de leitura, cada um com variante clara e escura. Nas 12, o texto fica em 7,4:1 ou mais. O limão nunca é usado como texto sobre o bege, porque ali o contraste é baixo.Six reading themes, each with a light and a dark variant. Across all 12, text stays at 7.4:1 or higher. Lime is never used as text on beige, because the contrast there is low.

09 · Onde está09 · Where it stands

O app está em desenvolvimento e ainda não tem usuários. A conta por venda fecha, mas ainda não há evidência de que as pessoas vão pagar.The app is in development and has no users yet. The per-sale math works, but there's no evidence yet that people will pay.

O básico vai ser grátis. O plano pago, de pagamento único, traz o que o painel pediu: ler PDF escaneado com reconhecimento de texto no aparelho, ouvir em voz alta e exportar destaques. Antes de construir, o plano é um teste de porta falsa no TestFlight e, antes disso, corrigir os três defeitos que hoje reprovam documentos: figura que engole a coluna vizinha, foto pequena que some e quadro sem grade que sai embaralhado.The basics will be free. The paid tier, a one-time purchase, brings what the panel asked for: reading scanned PDFs with on-device text recognition, listening out loud and exporting highlights. Before building it, the plan is a fake-door test on TestFlight and, before that, fixing the three defects that still fail documents today: a figure that swallows the next column, small photos that vanish and borderless tables that come out scrambled.

O principal aprendizado até aqui: em produto de conversão, a forma de medir vem antes do código. Sem o corpus, não haveria como saber se uma mudança melhorou o resultado.The main lesson so far: in a conversion product, the way of measuring comes before the code. Without the corpus, there would be no way to tell whether a change improved the result.

20
PDFs reais no corpus de testereal PDFs in the test corpus
12/20
documentos aprovados; a meta é 16documents passing; the target is 16
0
usuários, por enquantousers, for now