16

I've had a plain chat conversation and need display them similarly to how they are designed in Messenger. Since this is a long conversation, I need a method to do it at once. I know this might be impossible to have all, so I just need to mimic it as close as possible.

  • Left-aligned gray bubbles (103, 184, 104) with black text;
    Right-aligned green bubbles (241, 240, 240) with white text
  • Bubbles are only split after a paragraph (equivalent to an enter press when chatting). Long message with multiple lines will be kept in one bubble.
  • Left and right edges are half circle
  • Margins between same color bubbles are small, margins between different color bubbles are large.


FYI: What word processing tools that can work with complicate text boxing? in Graphic Design
How to wrap every paragraph with a custom text box in Word? in Super User

3
  • 2
    have a look on tcolobox package ctan.org/pkg/tcolorbox Commented Dec 8, 2017 at 5:22
  • Welcome to TeX.SX! Commented Dec 16, 2017 at 0:10
  • @Bobyandbob I don't know what else I should add Commented Dec 16, 2017 at 6:35

2 Answers 2

31

With tcolorbox:

\documentclass{article} \usepackage[many]{tcolorbox} \usepackage{xcolor} \usepackage{varwidth} \usepackage{environ} \usepackage{xparse} \newlength{\bubblewidth} \AtBeginDocument{\setlength{\bubblewidth}{.75\textwidth}} \definecolor{bubblegreen}{RGB}{103,184,104} \definecolor{bubblegray}{RGB}{241,240,240} \newcommand{\bubble}[4]{% \tcbox[ colback=#1, colframe=#1, #2, ]{\color{#3}\begin{varwidth}{\bubblewidth}#4\end{varwidth}}% } \ExplSyntaxOn \seq_new:N \l__ooker_bubbles_seq \tl_new:N \l__ooker_bubbles_first_tl \tl_new:N \l__ooker_bubbles_last_tl \NewEnviron{rightbubbles} { \raggedleft\sffamily \seq_set_split:NnV \l__ooker_bubbles_seq { \par } \BODY \int_compare:nTF { \seq_count:N \l__ooker_bubbles_seq < 2 } { \bubble{bubblegreen}{rounded~corners}{white}{\BODY} } { \seq_pop_left:NN \l__ooker_bubbles_seq \l__ooker_bubbles_first_tl \seq_pop_right:NN \l__ooker_bubbles_seq \l__ooker_bubbles_last_tl \bubble{bubblegreen}{sharp~corners=southeast}{white}{\l__ooker_bubbles_first_tl}\par \seq_map_inline:Nn \l__ooker_bubbles_seq { \bubble{bubblegreen}{sharp~corners=east}{white}{##1}\par } \bubble{bubblegreen}{sharp~corners=northeast}{white}{\l__ooker_bubbles_last_tl}\par } } \NewEnviron{leftbubbles} { \raggedright\sffamily \seq_set_split:NnV \l__ooker_bubbles_seq { \par } \BODY \int_compare:nTF { \seq_count:N \l__ooker_bubbles_seq < 2 } { \bubble{bubblegray}{rounded~corners}{black}{\BODY} } { \seq_pop_left:NN \l__ooker_bubbles_seq \l__ooker_bubbles_first_tl \seq_pop_right:NN \l__ooker_bubbles_seq \l__ooker_bubbles_last_tl \bubble{bubblegray}{sharp~corners=southwest}{black}{\l__ooker_bubbles_first_tl}\par \seq_map_inline:Nn \l__ooker_bubbles_seq { \bubble{bubblegray}{sharp~corners=west}{black}{##1}\par } \bubble{bubblegray}{sharp~corners=northwest}{black}{\l__ooker_bubbles_last_tl}\par } } \ExplSyntaxOff \begin{document} \begin{rightbubbles} Left-aligned gray bubbles (241, 240, 240) with black text Right-aligned green bubbles (103, 184,104) with white text Bubbles only break after a paragraph (equivalent to an enter press when chatting). Long message with multiple lines will be kept in one bubble. Left and right edges are round. \end{rightbubbles} \begin{leftbubbles} Left-aligned gray bubbles (241, 240, 240) with black text Right-aligned green bubbles (103, 184,104) with white text Bubbles only break after a paragraph (equivalent to an enter press when chatting). Long message with multiple lines will be kept in one bubble. Left and right edges are round. \end{leftbubbles} \begin{rightbubbles} Single \end{rightbubbles} \begin{leftbubbles} End \end{leftbubbles} \end{document} 

enter image description here

If I change the definition of \bubble to

\newcommand{\bubble}[4]{% \tcbox[ arc=5mm, colback=#1, colframe=#1, #2, ]{\color{#3}\begin{varwidth}{\bubblewidth}#4\end{varwidth}}% } 

then the output is as follows:

enter image description here

Improved version

You can define the spacing between bubbles by setting \bubblesep. Different color bubbles are enclosed in flushleft and flushright environments, so there will be \topsep space between them.

\documentclass{article} \usepackage[many]{tcolorbox} \usepackage{xcolor} \usepackage{varwidth} \usepackage{environ} \usepackage{xparse} \newlength{\bubblesep} \newlength{\bubblewidth} \setlength{\bubblesep}{2pt} \AtBeginDocument{\setlength{\bubblewidth}{.75\textwidth}} \definecolor{bubblegreen}{RGB}{103,184,104} \definecolor{bubblegray}{RGB}{241,240,240} \newcommand{\bubble}[4]{% \tcbox[ on line, arc=4.5mm, colback=#1, colframe=#1, #2, ]{\color{#3}\begin{varwidth}{\bubblewidth}#4\end{varwidth}}% } \ExplSyntaxOn \seq_new:N \l__ooker_bubbles_seq \tl_new:N \l__ooker_bubbles_first_tl \tl_new:N \l__ooker_bubbles_last_tl \NewEnviron{rightbubbles} { \begin{flushright} \sffamily \seq_set_split:NnV \l__ooker_bubbles_seq { \par } \BODY \int_compare:nTF { \seq_count:N \l__ooker_bubbles_seq < 2 } { \bubble{bubblegreen}{rounded~corners}{white}{\BODY}\par } { \seq_pop_left:NN \l__ooker_bubbles_seq \l__ooker_bubbles_first_tl \seq_pop_right:NN \l__ooker_bubbles_seq \l__ooker_bubbles_last_tl \bubble{bubblegreen}{sharp~corners=southeast}{white}{\l__ooker_bubbles_first_tl} \par\nointerlineskip \addvspace{\bubblesep} \seq_map_inline:Nn \l__ooker_bubbles_seq { \bubble{bubblegreen}{sharp~corners=east}{white}{##1} \par\nointerlineskip \addvspace{\bubblesep} } \bubble{bubblegreen}{sharp~corners=northeast}{white}{\l__ooker_bubbles_last_tl} \par } \end{flushright} } \NewEnviron{leftbubbles} { \begin{flushleft} \sffamily \seq_set_split:NnV \l__ooker_bubbles_seq { \par } \BODY \int_compare:nTF { \seq_count:N \l__ooker_bubbles_seq < 2 } { \bubble{bubblegray}{rounded~corners}{black}{\BODY}\par } { \seq_pop_left:NN \l__ooker_bubbles_seq \l__ooker_bubbles_first_tl \seq_pop_right:NN \l__ooker_bubbles_seq \l__ooker_bubbles_last_tl \bubble{bubblegray}{sharp~corners=southwest}{black}{\l__ooker_bubbles_first_tl} \par\nointerlineskip \addvspace{\bubblesep} \seq_map_inline:Nn \l__ooker_bubbles_seq { \bubble{bubblegray}{sharp~corners=west}{black}{##1} \par\nointerlineskip \addvspace{\bubblesep} } \bubble{bubblegray}{sharp~corners=northwest}{black}{\l__ooker_bubbles_last_tl}\par } \end{flushleft} } \ExplSyntaxOff \begin{document} \begin{rightbubbles} Left-aligned gray bubbles (241, 240, 240) with black text Right-aligned green bubbles (103, 184,104) with white text Bubbles only break after a paragraph (equivalent to an enter press when chatting). Long message with multiple lines will be kept in one bubble. Left and right edges are round. \end{rightbubbles} \begin{leftbubbles} Left-aligned gray bubbles (241, 240, 240) with black text Right-aligned green bubbles (103, 184,104) with white text Bubbles only break after a paragraph (equivalent to an enter press when chatting). Long message with multiple lines will be kept in one bubble. Left and right edges are round. \end{leftbubbles} \begin{rightbubbles} Single \end{rightbubbles} \begin{leftbubbles} End \end{leftbubbles} \end{document} 

enter image description here

12
  • thanks. Seems like there's no way to make the edges rounder, right? And why do you decide to use __ooker_bubbles_ string? It's nice to have my name, but isn't it long? Commented Dec 8, 2017 at 15:57
  • 2
    It's an expl3 convention that functions an variables should have a common prefix and a name. For the rounder edges I added an example. Commented Dec 8, 2017 at 16:13
  • Thanks. If we use arc=4.5mm the edges will be more natural. One more step: can the space between the same color bubbles be smaller? Another step: can the space between two different color bubble be larger? Commented Dec 8, 2017 at 17:07
  • I want to use \setlength{\parskip}{10pt} for normal text but it affects the bubble space. Do you know how to prevent this? Commented Dec 13, 2017 at 11:11
  • did you mean \color{#3}\setlength{\parskip}{0pt} in \newcommand{\bubble}? It doesn't work for me Commented Dec 13, 2017 at 12:04
16

In plain TeX (using pdfTeX) we can do this:

\font\ssf=cmssdc10 \def\Black{\pdfliteral{0 g}} \def\Blue{\pdfliteral{0 0 1 rg}} \def\White{\pdfliteral{1 g}} \def\bptopt#1{.9963 0 0 .9963 0 0 cm 17 0 0 #117 0 3.5 cm } \def\circle{.5 0 m .5 .276 .276 .5 0 .5 c -.276 .5 -.5 .276 -.5 0 c -.5 -.276 -.276 -.5 0 -.5 c .276 -.5 .5 -.276 .5 0 c } \def\hcircle{.5 .276 .276 .5 0 .5 c -.276 .5 -.5 .276 -.5 0 c } \def\fullround{\pdfliteral{q \bptopt+ \circle f Q}} \def\halfround{% \ifnum\count255=13 \pdfliteral{q \bptopt+ -.5 -.5 m .5 -.5 l .5 0 l \hcircle h f Q}% \else \ifnum\count255=14 \pdfliteral{q \bptopt- -.5 -.5 m .5 -.5 l .5 0 l \hcircle h f Q}% \else \pdfliteral{q \bptopt+ -.5 -.5 m -.5 .5 l .5 .5 l .5 -.5 l h f Q}% \fi\fi } \def\xround#1#2{\if #1#2\halfround \else \fullround\fi} \def\messenger#1#2{\medskip\setbox0=\vbox{\penalty13 \widowpenalty=14 \clubpenalty=0 \interlinepenalty=0 \def\\{\unskip\break}\rightskip=0pt plus 1fil\parindent=0pt \ssf #2\par \setbox1=\box2 \loop \setbox1=\lastbox \unless\ifvoid1 \unskip\unskip \count255=\lastpenalty \unpenalty \setbox1=\hbox{\unhbox1\unskip\unskip} \setbox1=\hbox to\hsize{\hss\Blue\xround l#1% \rlap{\vrule height12pt depth5pt width\wd1 \xround r#1}\White \box1 \Black \if l#1\hfill\fi} \global\setbox2=\vbox{\box1 \vskip1pt \unvbox2} \repeat } \unvbox2 \medskip } \hsize=9cm This is test: \messenger r { This is the native format from messenger \\ Notice, how the borders of the sides are circular, not straight and the last notice is here. } \messenger l { Second message \\ is here. } \bye 

Edit: I modified the code in order to enable to print two variants: left aligned, if \messenger l is used and right aligned, if \messenger r is used. As your comment says.

The result:

messenger2

Edit The specifications from OP were changed after my first result was published here. First, the colour was blue and bubbles were right aligned. Second, bubbles must be left aligned too. The last change of the specification includes a new request for colours and for formatting the paragraphs. I supposed that when there is my code published then slight changes of formatting a paragraphs or changing colours is just simple task for OP. But probably it cannot be expected because OP does not know what does mean \bye for example. Moreover, egreg presented second solution here based on a specific Expl3 language. He was in advantage because he knew newest specification from OP. I cannot keep only Expl3 solution here, because it is obscure language from my point of view which cannot help to teach the basics TeX principles. So, there is my second solution using TeX primitives and accepting the new specification form OP. I hope that there will be no more specification changes from OP.

\font\ssf=cmssdc10 \def\Black{\pdfliteral{0 g}} \def\Blue{\pdfliteral{0 0 1 rg}} \def\White{\pdfliteral{1 g}} \def\Gray{\pdfliteral{.945 .941 .941 rg}} \def\Green{\pdfliteral{.404 .722 .408 rg}} \def\bcolor#1{\if r#1\Green \else \Gray \fi} \def\fcolor#1{\if r#1\White \else \Black \fi} \def\bptopt#1{.9963 0 0 .9963 0 0 cm 17 0 0 #117 0 3.5 cm } \def\circle{.5 0 m .5 .276 .276 .5 0 .5 c -.276 .5 -.5 .276 -.5 0 c -.5 -.276 -.276 -.5 0 -.5 c .276 -.5 .5 -.276 .5 0 c } \def\hcircle{.5 .276 .276 .5 0 .5 c -.276 .5 -.5 .276 -.5 0 c } \def\fullround{\pdfliteral{q \bptopt+ \circle f Q}} \def\halfround{% \ifcase\count255 \pdfliteral{q \bptopt+ -.5 -.5 m -.5 .5 l .5 .5 l .5 -.5 l h f Q}% \or \pdfliteral{q \bptopt+ -.5 -.5 m .5 -.5 l .5 0 l \hcircle h f Q}% \or \pdfliteral{q \bptopt- -.5 -.5 m .5 -.5 l .5 0 l \hcircle h f Q}% \or \fullround \fi } \def\xround#1#2{\ifdim\ht1>12pt \dimen0=\ht1 \advance\dimen0 by-12pt \hbox to0pt{\hss\vbox{\xroundA#1#2\kern\dimen0 \xroundA#1#2}% \rlap{\kern-8.5pt\raise4.5pt\vbox{\hrule width17pt height\dimen0}}\hss}% \else \xroundA#1#2\fi} \def\xroundA#1#2{\if #1#2\halfround \else \fullround\fi} \long\def\messenger#1#2{\medskip\setbox0=\vbox{\penalty13 \widowpenalty=0 \clubpenalty=0 \interlinepenalty=0 \everypar={\setbox0=\lastbox\endgraf\vbox\bgroup \everypar={\vrule height12pt width0pt}} \def\par{\ifhmode\endgraf\egroup\fi} \def\\{\unskip\break}\rightskip=0pt plus 1fil\parindent=0pt \ssf #2\par \everypar={}\let\par=\endgraf \setbox1=\box2 \loop \setbox1=\lastbox \unless\ifvoid1 \unskip\unskip \count255=\ifvoid2 \ifnum\lastpenalty=13 3\else 2\fi \else \ifnum\lastpenalty=13 1\else 0\fi \fi \unpenalty \ifdim\ht1>12pt \else \setbox0=\vbox{\unvbox1 \lastbox \setbox1=\lastbox \global\setbox1=\hbox{\unhbox1\unskip\unskip}} \fi \setbox1=\hbox to\hsize{\hss\bcolor#1\xround l#1% \rlap{\vrule height\ht1 depth5pt width\wd1 \xround r#1}% \fcolor#1\box1 \Black \if l#1\hfill\fi} \global\setbox2=\vbox{\box1 \vskip2pt \unvbox2} \repeat } \unvbox2 \medskip } \hsize=9cm This is test: \messenger r { Left-aligned gray bubbles (241, 240, 240) with black text Right-aligned green bubbles (103, 184,104) with white text Bubbles only break after a paragraph (equivalent to an enter press when chatting). Long message with multiple lines will be kept in one bubble. Left and right edges are round. } \messenger l { Left-aligned gray bubbles (241, 240, 240) with black text Right-aligned green bubbles (103, 184,104) with white text Bubbles only break after a paragraph (equivalent to an enter press when chatting). Long message with multiple lines will be kept in one bubble. } \messenger r { Single } \messenger l { End } \bye 

messenger3

9
  • 2
    \bye is defined in plain TeX. I mentioned that this was tested in plain TeX in my answer. I don't use LaTeX. If you are using LaTeX then you must remove \bye (of course) and you must add tons of another lines of code and you must pray that it will work. Commented Dec 8, 2017 at 14:36
  • 1
    I am very happy to see you around, Petr! :) Commented Dec 8, 2017 at 16:41
  • 1
    you are using e-TeX's \unless? that's a bit modernistic ;-) Commented Dec 8, 2017 at 16:41
  • 1
    @jfbu The \pdfliteral seems to be modernistic too. On the other hand, LaTeX kernel seems to be a bit ancient from my point of view. ;-) Commented Dec 8, 2017 at 19:31
  • 1
    It's also nice that (the first version of) this solution is half the length of the (first) expl3 one - but neither is particularly easy to understand :) Commented Dec 9, 2017 at 10:57

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.