9

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

22

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

  • 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? – Ooker Dec 8 '17 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. – egreg Dec 8 '17 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? – Ooker Dec 8 '17 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? – Ooker Dec 13 '17 at 11:11
  • did you mean \color{#3}\setlength{\parskip}{0pt} in \newcommand{\bubble}? It doesn't work for me – Ooker Dec 13 '17 at 12:04
13

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

  • I've update my question. Can you make different color for different bubbles, and long message with multiple lines will be kept in one bubble? Thanks – Ooker Dec 8 '17 at 11:57
  • also, what does \bye do? I actually have to delete it to run the script – Ooker Dec 8 '17 at 13:53
  • 1
    \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. – wipet Dec 8 '17 at 14:36
  • I am very happy to see you around, Petr! :) – Paulo Cereda Dec 8 '17 at 16:41
  • 1
    you are using e-TeX's \unless? that's a bit modernistic ;-) – user4686 Dec 8 '17 at 16:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.