Please create a colorful handbook pdf using information from both the attached file and also extra information from your side too of course.The colorful handbook pdf should be strictly be prepared only one topic " Participle clauses" .It must be very easy and creative to understand for my C1 proficiency ESL student who is learning "Participle clauses" for the first time. Please add Russian translation under every sentence of information material and also please add 5 daily life examples sentences in English and under them Russian translation please.

Reading File
Running Command

cd /tmp/workspace/attachments/32cd4907-e41f-46df-9309-106aecaf3459/ && python -c " import pdfplumber for f in ['DOC-20261030-WA0030.pdf','DOC-20261030-WA0031.pdf']: with pdfplumber.open(f) as p: print('=====',f,len(p.pages)) for pg in p.pages: print(pg.extract_text()); print('--page--') "; ls /tmp/workspace

Running Command

fc-list 2>/dev/null | grep -iE "dejavu|noto|liberation|roboto|open" | head -20; ls /usr/share/fonts/truetype 2>/dev/null; python -c "import matplotlib,os;print(os.path.join(os.path.dirname(matplotlib.__file__),'mpl-data/fonts/ttf'))"; ls /tmp/workspace

Running Command

mkdir -p /tmp/workspace/participle-handbook/src /tmp/workspace/participle-handbook/output && cat > /tmp/workspace/participle-handbook/src/build.py <<'EOF' # -*- coding: utf-8 -*- import os, matplotlib from reportlab.lib.pagesizes import A4 from reportlab.lib import colors from reportlab.lib.colors import HexColor from reportlab.lib.styles import ParagraphStyle from reportlab.lib.enums import TA_CENTER, TA_LEFT from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont from reportlab.platypus import (BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table, TableStyle, PageBreak, NextPageTemplate, KeepTogether) fd = os.path.join(os.path.dirname(matplotlib.__file__), 'mpl-data/fonts/ttf') for n, f in [('DV','DejaVuSans.ttf'),('DV-B','DejaVuSans-Bold.ttf'),('DV-I','DejaVuSans-Oblique.ttf'),('DV-BI','DejaVuSans-BoldOblique.ttf')]: pdfmetrics.registerFont(TTFont(n, os.path.join(fd, f))) pdfmetrics.registerFontFamily('DV', normal='DV', bold='DV-B', italic='DV-I', boldItalic='DV-BI') # palette CORAL=HexColor('#FF6B6B'); ORANGE=HexColor('#FF9F1C'); YELLOW=HexColor('#FFD93D'); GREEN=HexColor('#2EC4B6') BLUE=HexColor('#3A86FF'); PURPLE=HexColor('#8338EC'); PINK=HexColor('#FF5D8F'); NAVY=HexColor('#1D3557') L_CORAL=HexColor('#FFE3E3'); L_ORANGE=HexColor('#FFF0D6'); L_YELLOW=HexColor('#FFF8D0'); L_GREEN=HexColor('#D9F7F3') L_BLUE=HexColor('#DEEBFF'); L_PURPLE=HexColor('#EBDDFF'); L_PINK=HexColor('#FFE0EB'); CREAM=HexColor('#FFFBF0') RUC='#6A3FB5' PW, PH = A4 M = 42 W = PW - 2*M def S(name, **kw): base = dict(fontName='DV', fontSize=10, leading=14, textColor=NAVY) base.update(kw); return ParagraphStyle(name, **base) st_en = S('en', fontSize=10.5, leading=15) st_ru = S('ru', fontName='DV-I', fontSize=9.2, leading=13, textColor=HexColor(RUC), spaceAfter=5) st_enc = S('enc', fontSize=10.5, leading=15, alignment=TA_CENTER) st_ruc = S('ruc', fontName='DV-I', fontSize=9.2, leading=13, textColor=HexColor(RUC), alignment=TA_CENTER, spaceAfter=4) st_sm = S('sm', fontSize=9.2, leading=13) st_smru = S('smru', fontName='DV-I', fontSize=8.4, leading=12, textColor=HexColor(RUC), spaceAfter=4) st_h = S('h', fontName='DV-B', fontSize=16, leading=20, textColor=colors.white) st_hru = S('hru', fontName='DV-I', fontSize=10, leading=13, textColor=colors.white) st_lab = S('lab', fontName='DV-B', fontSize=11, leading=14) st_labru= S('labru', fontName='DV-I', fontSize=8.8, leading=12, textColor=HexColor(RUC), spaceAfter=3) st_th = S('th', fontName='DV-B', fontSize=9.5, leading=12, textColor=colors.white) st_thru = S('thru', fontName='DV-I', fontSize=8.2, leading=11, textColor=colors.white) def hl(t, c='#E63946'): return f'<font color="{c}"><b>{t}</b></font>' def pair(en, ru, center=False, small=False): if small: return [Paragraph(en, st_sm), Paragraph(ru, st_smru)] return [Paragraph(en, st_enc if center else st_en), Paragraph(ru, st_ruc if center else st_ru)] def card(flows, bg, border, width=W, pad=9): t = Table([[flows]], colWidths=[width]) t.setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),bg),('BOX',(0,0),(-1,-1),1.6,border), ('ROUNDEDCORNERS',[10,10,10,10]),('LEFTPADDING',(0,0),(-1,-1),pad+2),('RIGHTPADDING',(0,0),(-1,-1),pad+2), ('TOPPADDING',(0,0),(-1,-1),pad),('BOTTOMPADDING',(0,0),(-1,-1),pad-2)])) return t def banner(num, title, ru, color): left = Table([[Paragraph(f'<para alignment="center"><font name="DV-B" size="17" color="{color.hexval().replace("0x","#")}">{num}</font></para>', st_en)]], colWidths=[34], rowHeights=[34]) left.setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),colors.white),('ROUNDEDCORNERS',[17,17,17,17]),('VALIGN',(0,0),(-1,-1),'MIDDLE')])) t = Table([[left, [Paragraph(title, st_h), Paragraph(ru, st_hru)]]], colWidths=[48, W-48]) t.setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),color),('ROUNDEDCORNERS',[12,12,12,12]),('VALIGN',(0,0),(-1,-1),'MIDDLE'), ('LEFTPADDING',(0,0),(0,0),8),('TOPPADDING',(0,0),(-1,-1),8),('BOTTOMPADDING',(0,0),(-1,-1),8)])) return t def sp(h=8): return Spacer(1, h) # ---------- page decorations ---------- def content_page(c, doc): c.saveState() bands = [CORAL, ORANGE, YELLOW, GREEN, BLUE, PURPLE, PINK] bw = PW/len(bands) for i, b in enumerate(bands): c.setFillColor(b); c.rect(i*bw, PH-14, bw, 14, stroke=0, fill=1) c.setFillColor(HexColor('#FFFBF0')) c.setFont('DV-B', 8); c.setFillColor(NAVY) c.drawString(M, PH-30, 'PARTICIPLE CLAUSES') c.setFont('DV-I', 8); c.setFillColor(HexColor(RUC)) c.drawRightString(PW-M, PH-30, 'Причастные обороты | C1 Handbook') # footer for i, b in enumerate(bands): c.setFillColor(b); c.circle(PW/2 - 36 + i*12, 22, 4, stroke=0, fill=1) c.setFont('DV-B', 9); c.setFillColor(NAVY) c.drawRightString(PW-M, 18, str(doc.page)) c.restoreState() def cover_page(c, doc): c.saveState() c.setFillColor(CREAM); c.rect(0,0,PW,PH,stroke=0,fill=1) # big purple header c.setFillColor(PURPLE); c.roundRect(-20, 470, PW+40, 400, 40, stroke=0, fill=1) # decorative circles for (x,y,r,col) in [(60,790,38,CORAL),(540,800,30,YELLOW),(500,520,45,PINK),(90,500,26,GREEN),(300,820,20,ORANGE),(560,640,18,GREEN),(40,640,16,YELLOW)]: c.setFillColor(col); c.circle(x,y,r,stroke=0,fill=1) c.setFillColor(colors.white) c.setFont('DV-B', 13); c.drawCentredString(PW/2, 740, 'A COLOURFUL HANDBOOK | C1 ESL') c.setFont('DV-B', 50); c.drawCentredString(PW/2, 670, 'PARTICIPLE') c.setFillColor(YELLOW); c.drawCentredString(PW/2, 610, 'CLAUSES') c.setFillColor(colors.white); c.setFont('DV-I', 17); c.drawCentredString(PW/2, 568, 'Причастные обороты') c.setFont('DV', 10.5); c.drawCentredString(PW/2, 530, 'Short, smart and stylish: say more with fewer words!') c.setFont('DV-I', 9.5); c.drawCentredString(PW/2, 514, 'Коротко, умно и стильно: говорите больше, используя меньше слов!') # sentence anatomy y = 395 c.setFont('DV-B', 12); c.setFillColor(NAVY); c.drawCentredString(PW/2, y+55, 'Sentence anatomy / Строение предложения') c.setFillColor(CORAL); c.roundRect(52, y, 230, 42, 12, stroke=0, fill=1) c.setFillColor(GREEN); c.roundRect(300, y, 243, 42, 12, stroke=0, fill=1) c.setFillColor(colors.white); c.setFont('DV-B', 13) c.drawCentredString(167, y+16, 'Walking home,') c.drawCentredString(421, y+16, 'I met my teacher.') c.setFont('DV-B', 8.5); c.setFillColor(NAVY) c.drawCentredString(167, y-14, 'PARTICIPLE CLAUSE') c.drawCentredString(421, y-14, 'MAIN CLAUSE') c.setFont('DV-I', 8.5); c.setFillColor(HexColor(RUC)) c.drawCentredString(167, y-27, 'причастный оборот') c.drawCentredString(421, y-27, 'главное предложение') c.setFont('DV-I', 10); c.drawCentredString(PW/2, y-52, 'Идя домой, я встретил своего учителя.') # chips chips = [('1','What is it?',CORAL),('2','The forms',ORANGE),('3','Golden rule',YELLOW),('4','Meanings',GREEN), ('5','With nouns',BLUE),('6','Mistakes',PURPLE),('7','Daily life',PINK),('8','Practice',CORAL)] cx = 52; cy = 255 c.setFont('DV-B', 11); c.setFillColor(NAVY); c.drawCentredString(PW/2, cy+38, 'Inside this handbook / Что внутри') for i,(n,t,col) in enumerate(chips): row, colm = divmod(i,4) x = 52 + colm*125; yy = cy - row*44 c.setFillColor(col); c.roundRect(x, yy, 118, 34, 10, stroke=0, fill=1) c.setFillColor(colors.white if col not in (YELLOW,) else NAVY) c.setFont('DV-B', 11); c.drawString(x+10, yy+12, n); c.setFont('DV-B', 9.5); c.drawString(x+26, yy+12, t) c.setFont('DV-I', 8.5); c.setFillColor(HexColor(RUC)) c.drawCentredString(PW/2, 125, 'Based on your coursebook pages (Lesson 5B and Relative Clauses) plus extra explanations.') c.drawCentredString(PW/2, 112, 'Основано на страницах вашего учебника (урок 5B и относительные придаточные) и дополнено объяснениями.') c.restoreState() doc = BaseDocTemplate('/tmp/workspace/participle-handbook/output/Participle_Clauses_Handbook.pdf', pagesize=A4, leftMargin=M, rightMargin=M, topMargin=48, bottomMargin=40, title='Participle Clauses: A Colourful C1 Handbook', author='Orris') doc.addPageTemplates([ PageTemplate(id='cover', frames=[Frame(0,0,1,1,id='x')], onPage=cover_page), PageTemplate(id='content', frames=[Frame(M, 40, W, PH-48-40, id='f', leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0)], onPage=content_page)]) st = [] st += [NextPageTemplate('content'), PageBreak()] # ================= 1 ================= st.append(banner('1', 'What is a participle clause?', 'Что такое причастный оборот?', CORAL)) st.append(sp(10)) f = [] f += pair('A participle clause is a short clause that starts with an ' + hl('-ing form') + ' or a ' + hl('past participle') + '.', 'Причастный оборот - это короткая часть предложения, которая начинается с формы -ing или с причастия прошедшего времени (третья форма глагола).') f += pair('We use it to add information to the main clause or to a noun.', 'Мы используем его, чтобы добавить информацию к главному предложению или к существительному.') f += pair('The main clause gives the subject and the tense (time) of the participle clause.', 'Главное предложение задаёт подлежащее и время для причастного оборота.') f += pair('Think of it as a shortcut: fewer words, same meaning!', 'Считайте это сокращённой дорогой: меньше слов, тот же смысл!') st.append(card(f, L_CORAL, CORAL)) st.append(sp(12)) st.append(Paragraph('The shortcut machine / Машина сокращений', S('t1', fontName='DV-B', fontSize=12, textColor=CORAL))) st.append(sp(4)) def machine(long_en, long_ru, short_en, short_ru): left = [Paragraph('LONG / длинно', S('x', fontName='DV-B', fontSize=8, textColor=CORAL))] + pair(long_en, long_ru, small=True) right= [Paragraph('SHORT / коротко', S('x2', fontName='DV-B', fontSize=8, textColor=GREEN))] + pair(short_en, short_ru, small=True) arrow = Paragraph('<para alignment="center"><font name="DV-B" size="22" color="#FF9F1C">&#8594;</font></para>', st_en) t = Table([[left, arrow, right]], colWidths=[W*0.43, W*0.1, W*0.43]) t.setStyle(TableStyle([('VALIGN',(0,0),(-1,-1),'MIDDLE'),('BACKGROUND',(0,0),(0,0),colors.white),('BACKGROUND',(2,0),(2,0),L_GREEN), ('BOX',(0,0),(0,0),1,CORAL),('BOX',(2,0),(2,0),1,GREEN),('ROUNDEDCORNERS',[8,8,8,8]), ('LEFTPADDING',(0,0),(-1,-1),6),('RIGHTPADDING',(0,0),(-1,-1),6),('TOPPADDING',(0,0),(-1,-1),5),('BOTTOMPADDING',(0,0),(-1,-1),2)])) return t st.append(machine('Because she was tired, she went to bed.', 'Так как она устала, она легла спать.', hl('Being tired', '#2EC4B6') + ', she went to bed.', 'Устав, она легла спать.')) st.append(sp(6)) st.append(machine('While I was waiting for the bus, I read the news.', 'Пока я ждал автобус, я читал новости.', hl('Waiting for the bus', '#2EC4B6') + ', I read the news.', 'Ожидая автобус, я читал новости.')) st.append(sp(6)) st.append(machine('The woman who is sitting in front of you is my cousin.', 'Женщина, которая сидит перед вами, моя двоюродная сестра.', 'The woman ' + hl('sitting in front of you', '#2EC4B6') + ' is my cousin.', 'Женщина, сидящая перед вами, моя двоюродная сестра.')) st.append(sp(12)) f = [Paragraph('<font name="DV-B" color="#3A86FF">Russian helper / Подсказка для русскоговорящих</font>', st_lab)] f += pair('Russian has two tools: the participle (идущий) and the verbal adverb (идя). English uses one family of forms for both jobs.', 'В русском есть два инструмента: причастие (идущий) и деепричастие (идя). В английском для обеих задач используется одно семейство форм.') f += pair('Walking = идя / идущий. Having walked = пройдя. Not walking = не идя. Walked (passive) = пройденный.', 'Walking = идя / идущий. Having walked = пройдя. Not walking = не идя. Walked (в пассиве) = пройденный.') f += pair('Good news: a participle clause in English works much like a деепричастный оборот in Russian!', 'Хорошая новость: причастный оборот в английском работает очень похоже на деепричастный оборот в русском!') st.append(card(f, L_BLUE, BLUE)) st.append(PageBreak()) # ================= 2 ================= st.append(banner('2', 'Meet the forms', 'Знакомьтесь с формами', ORANGE)) st.append(sp(10)) def cell(*flows): return list(flows) def th(en, ru): return [Paragraph(en, st_th), Paragraph(ru, st_thru)] rows = [[th('FORM','ФОРМА'), th('HOW IT WORKS','КАК ЭТО РАБОТАЕТ'), th('EXAMPLE','ПРИМЕР')]] data = [ (CORAL, L_CORAL, 'Present participle<br/>V-ing', 'Настоящее причастие, V-ing', 'The subject does the action. It often happens at the same time as the main action.', 'Подлежащее само выполняет действие. Часто оно происходит одновременно с главным действием.', hl('Thinking', '#FF6B6B') + ' about it now, I don\'t remember ever being bored at home.', 'Думая об этом сейчас, я не помню, чтобы мне когда-либо было скучно дома.'), (ORANGE, L_ORANGE, 'Past participle<br/>V3', 'Причастие прошедшего времени, V3', 'It has a passive meaning: something is done to the subject.', 'У неё страдательное значение: что-то делают с подлежащим.', hl('Left', '#FF9F1C') + ' to my own devices, I\'d spend all day lounging around at home.', 'Предоставленный сам себе, я бы весь день валялся дома.'), (GREEN, L_GREEN, 'Perfect participle<br/>having + V3', 'Перфектное причастие, having + V3', 'It shows that the participle action happened BEFORE the main action.', 'Оно показывает, что действие причастного оборота произошло РАНЬШЕ главного действия.', hl('Having spent', '#2EC4B6') + ' all day indoors, I really need to get out.', 'Проведя весь день в помещении, я просто обязан выйти на улицу.'), (BLUE, L_BLUE, 'Perfect passive<br/>having been + V3', 'Перфектный пассив, having been + V3', 'Earlier action + passive meaning (something was done to the subject).', 'Более раннее действие + страдательное значение (что-то сделали с подлежащим).', hl('Having been ignored', '#3A86FF') + ' by the adults, the kids invented their own games.', 'Оставшись без внимания взрослых, дети придумали свои игры.'), (PURPLE, L_PURPLE, 'Negative<br/>not + participle', 'Отрицание, not + причастие', 'Put NOT directly before the participle.', 'Ставьте NOT прямо перед причастием.', hl('Not having grown up', '#8338EC') + ' in a city, I find these activities strange.', 'Так как я не вырос в городе, эти занятия кажутся мне странными.'), ] for (col, lcol, f_en, f_ru, h_en, h_ru, ex_en, ex_ru) in data: rows.append([[Paragraph(f'<font name="DV-B" size="9.5">{f_en}</font>', st_sm), Paragraph(f_ru, st_smru)], pair(h_en, h_ru, small=True), pair(ex_en, ex_ru, small=True)]) t = Table(rows, colWidths=[W*0.24, W*0.33, W*0.43], repeatRows=1) sty = [('BACKGROUND',(0,0),(-1,0),NAVY),('VALIGN',(0,0),(-1,-1),'TOP'),('GRID',(0,0),(-1,-1),0.6,colors.white), ('LEFTPADDING',(0,0),(-1,-1),6),('RIGHTPADDING',(0,0),(-1,-1),6),('TOPPADDING',(0,0),(-1,-1),6),('BOTTOMPADDING',(0,0),(-1,-1),4), ('ROUNDEDCORNERS',[8,8,8,8])] for i, d in enumerate(data, start=1): sty += [('BACKGROUND',(0,i),(0,i),d[1]),('BACKGROUND',(1,i),(2,i),HexColor('#FFFFFF')),('LINEBEFORE',(0,i),(0,i),5,d[0]),('BOX',(0,i),(-1,i),0.8,d[0])] t.setStyle(TableStyle(sty)) st.append(t) st.append(sp(12)) f = [Paragraph('<font name="DV-B" color="#FF9F1C">Quick recipe / Быстрый рецепт</font>', st_lab)] f += pair('Active meaning = V-ing. Passive meaning = V3. Earlier action = having + V3. Negative = not first.', 'Действует само = V-ing. С ним что-то делают = V3. Более раннее действие = having + V3. Отрицание = not в начале.') f += pair('The verb "be" can also appear: Being tired = Feeling tired. Being is often dropped: (Being) left alone, she cried.', 'Глагол "be" тоже может появляться: Being tired = Чувствуя усталость. Being часто опускают: (Being) left alone, she cried.') st.append(card(f, L_ORANGE, ORANGE)) st.append(sp(10)) # time line visual tl = [Paragraph('<font name="DV-B" color="#2EC4B6">Time travel check / Проверка по времени</font>', st_lab)] tl += pair(hl('Walking', '#FF6B6B') + ' to the station, I called Mum. (same time)', 'Идя на станцию, я позвонил маме. (одновременно)', small=True) tl += pair(hl('Having missed', '#2EC4B6') + ' the train, I called Mum. (first the train, then the call)', 'Опоздав на поезд, я позвонил маме. (сначала поезд, потом звонок)', small=True) st.append(card(tl, L_GREEN, GREEN)) st.append(PageBreak()) # ================= 3 ================= st.append(banner('3', 'The Golden Rule: same subject', 'Золотое правило: одно и то же подлежащее', YELLOW)) st[-1].setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),ORANGE),('ROUNDEDCORNERS',[12,12,12,12]),('VALIGN',(0,0),(-1,-1),'MIDDLE'), ('LEFTPADDING',(0,0),(0,0),8),('TOPPADDING',(0,0),(-1,-1),8),('BOTTOMPADDING',(0,0),(-1,-1),8)])) st.append(sp(10)) f = [] f += pair('The person or thing doing the action in the participle clause must be the SUBJECT of the main clause.', 'Тот, кто выполняет действие в причастном обороте, должен быть ПОДЛЕЖАЩИМ главного предложения.') f += pair('Ask yourself: "Who is doing the -ing action?" If the answer is not the main subject, rewrite the sentence.', 'Спросите себя: "Кто выполняет действие -ing?" Если это не подлежащее главного предложения, перепишите фразу.') f += pair('Russian has the same rule for the деепричастие, so you already know this idea!', 'В русском такое же правило действует для деепричастия, так что эта идея вам уже знакома!') st.append(card(f, L_YELLOW, ORANGE)) st.append(sp(12)) def tick(ok, en, ru, note_en='', note_ru=''): col = GREEN if ok else CORAL; lcol = L_GREEN if ok else L_CORAL sym = '&#10004;' if ok else '&#10008;' left = Paragraph(f'<para alignment="center"><font name="DV-B" size="20" color="white">{sym}</font></para>', st_en) fl = pair(en, ru) if note_en: fl += pair(note_en, note_ru, small=True) t = Table([[left, fl]], colWidths=[36, W-36]) t.setStyle(TableStyle([('BACKGROUND',(0,0),(0,0),col),('BACKGROUND',(1,0),(1,0),lcol),('VALIGN',(0,0),(0,0),'MIDDLE'), ('ROUNDEDCORNERS',[8,8,8,8]),('LEFTPADDING',(1,0),(1,0),9),('TOPPADDING',(0,0),(-1,-1),6),('BOTTOMPADDING',(0,0),(-1,-1),2)])) return t st.append(tick(True, hl('Walking home', '#2EC4B6') + ', I met my teacher.', 'Идя домой, я встретил своего учителя.', 'I walk and I meet: the same person!', 'Я иду и я встречаю: это один и тот же человек!')) st.append(sp(6)) st.append(tick(False, hl('Walking home', '#FF6B6B') + ', my phone rang.', 'Идя домой, мой телефон зазвонил. (звучит странно!)', 'The phone was not walking!', 'Телефон же не шёл домой!')) st.append(sp(6)) st.append(tick(True, hl('Walking home', '#2EC4B6') + ', I heard my phone ring.', 'Идя домой, я услышал, как зазвонил мой телефон.', 'Fixed: now "I" is the subject.', 'Исправлено: теперь подлежащее "I".')) st.append(sp(14)) st.append(Paragraph('Joining words can stay! / Союзы можно оставить!', S('t3', fontName='DV-B', fontSize=12, textColor=PURPLE))) st.append(sp(4)) f = [] f += pair('After, before, while, since, when and on can stay in front of the -ing form to make the time or link clearer.', 'Слова after, before, while, since, when и on можно оставить перед формой -ing, чтобы время или связь были яснее.') f += pair(hl('Before leaving', '#8338EC') + ', check that the lights are off.', 'Перед уходом проверьте, что свет выключен.') f += pair(hl('While waiting', '#8338EC') + ' for my coffee, I answered three emails.', 'Пока я ждал кофе, я ответил на три письма.') f += pair(hl('Since moving', '#8338EC') + ' to London, she has made lots of friends.', 'С тех пор как она переехала в Лондон, она завела много друзей.') f += pair(hl('On arriving', '#8338EC') + ', please call me.', 'По прибытии, пожалуйста, позвоните мне.') st.append(card(f, L_PURPLE, PURPLE)) st.append(PageBreak()) # ================= 4 ================= st.append(banner('4', 'What can a participle clause mean?', 'Что может означать причастный оборот?', GREEN)) st.append(sp(8)) st.append(Paragraph('Your coursebook (Lesson 5B) shows four hidden meanings. Detective time!', st_en)) st.append(Paragraph('В вашем учебнике (урок 5B) показаны четыре скрытых значения. Время стать детективом!', st_ru)) def meaning(title, ru_title, key, col, lcol, ex_en, ex_ru, full_en, full_ru): head = [Paragraph(f'<font name="DV-B" size="12" color="white">{title}</font>', st_en), Paragraph(f'<font name="DV-I" size="8.8" color="white">{ru_title}</font>', st_en)] k = Paragraph(f'<font name="DV-B" size="9" color="white">{key}</font>', st_en) body = pair(ex_en, ex_ru) + pair(full_en, full_ru, small=True) t = Table([[head, body]], colWidths=[W*0.27, W*0.73]) t.setStyle(TableStyle([('BACKGROUND',(0,0),(0,0),col),('BACKGROUND',(1,0),(1,0),lcol),('VALIGN',(0,0),(0,0),'MIDDLE'),('VALIGN',(1,0),(1,0),'TOP'), ('ROUNDEDCORNERS',[10,10,10,10]),('LEFTPADDING',(0,0),(-1,-1),9),('TOPPADDING',(0,0),(-1,-1),7),('BOTTOMPADDING',(0,0),(-1,-1),2)])) return t st.append(sp(6)) st.append(meaning('RESULT', 'результат: "so / which meant"', '', CORAL, L_CORAL, 'We used to be ignored by the adults at family gatherings, ' + hl('having to entertain', '#E63946') + ' ourselves.', 'Взрослые обычно не обращали на нас внимания на семейных встречах, и нам приходилось развлекать себя самим.', '= ... which meant we had to entertain ourselves.', '= ... а это значило, что нам приходилось развлекать себя самим.')) st.append(sp(7)) st.append(meaning('REASON', 'причина: "because"', '', ORANGE, L_ORANGE, hl('Not having grown up', '#E07B00') + ' in a city, I find these activities particularly strange.', 'Поскольку я не вырос в городе, эти занятия кажутся мне особенно странными.', '= Because I didn\'t grow up in a city, ...', '= Потому что я не вырос в городе, ...')) st.append(sp(7)) st.append(meaning('TIME', 'время: "while / as"', '', GREEN, L_GREEN, hl('Reflecting', '#17998D') + ' on these memories, we create a narrative which allows us to make sense of life.', 'Размышляя об этих воспоминаниях, мы создаём рассказ, который помогает нам осмыслить жизнь.', '= While we reflect on these memories, ...', '= Пока мы размышляем об этих воспоминаниях, ...')) st.append(sp(7)) st.append(meaning('CONDITION', 'условие: "if / when"', '', BLUE, L_BLUE, hl('Faced', '#2563D6') + ' with that terrible whine, maybe we should take a leaf out of our grandparents\' book.', 'Столкнувшись с этим ужасным нытьём, возможно, нам стоит взять пример с наших бабушек и дедушек.', '= If / When we are faced with that terrible whine, ...', '= Если / Когда мы сталкиваемся с этим ужасным нытьём, ...')) st.append(sp(7)) st.append(meaning('CONTRAST', 'уступка: "although"', '', PURPLE, L_PURPLE, hl('Having studied', '#6D28D9') + ' for hours, he still failed the test.', 'Хотя он учился несколько часов, он всё равно не сдал тест.', '= Although he had studied for hours, ... (extra meaning from us)', '= Хотя он учился несколько часов, ... (дополнительное значение от нас)')) st.append(sp(8)) f = [Paragraph('<font name="DV-B" color="#2EC4B6">Detective tip / Совет детектива</font>', st_lab)] f += pair('The participle clause alone does not say which meaning it has. The context decides.', 'Сам по себе причастный оборот не говорит, какое у него значение. Это определяет контекст.') f += pair('Try replacing it with because, while, if or so: the one that sounds natural is your answer.', 'Попробуйте заменить его на because, while, if или so: тот вариант, который звучит естественно, и есть ответ.') st.append(card(f, L_GREEN, GREEN)) st.append(PageBreak()) # ================= 5 ================= st.append(banner('5', 'Participle clauses with nouns', 'Причастные обороты с существительными', BLUE)) st.append(sp(10)) f = [] f += pair('A relative clause (who, which, that) can be shortened to a participle phrase.', 'Относительное придаточное (who, which, that) можно сократить до причастной фразы.') f += pair('Delete the pronoun and the auxiliary verb. Active meaning gives -ing. Passive meaning gives V3.', 'Уберите местоимение и вспомогательный глагол. Активное значение даёт -ing. Пассивное значение даёт V3.') st.append(card(f, L_BLUE, BLUE)) st.append(sp(10)) # machine steps steps = Table([[Paragraph('<para alignment="center"><font name="DV-B" size="9" color="white">STEP 1</font><br/><font name="DV" size="8.5" color="white">Find who / which + verb</font><br/><font name="DV-I" size="7.8" color="white">Найдите who / which + глагол</font></para>', st_en), Paragraph('<para alignment="center"><font name="DV-B" size="9" color="white">STEP 2</font><br/><font name="DV" size="8.5" color="white">Delete who / which + is / are / was</font><br/><font name="DV-I" size="7.8" color="white">Удалите who / which + is / are / was</font></para>', st_en), Paragraph('<para alignment="center"><font name="DV-B" size="9" color="white">STEP 3</font><br/><font name="DV" size="8.5" color="white">Active: V-ing. Passive: V3</font><br/><font name="DV-I" size="7.8" color="white">Актив: V-ing. Пассив: V3</font></para>', st_en)]], colWidths=[W/3]*3) steps.setStyle(TableStyle([('BACKGROUND',(0,0),(0,0),CORAL),('BACKGROUND',(1,0),(1,0),ORANGE),('BACKGROUND',(2,0),(2,0),GREEN), ('ROUNDEDCORNERS',[10,10,10,10]),('VALIGN',(0,0),(-1,-1),'MIDDLE'),('TOPPADDING',(0,0),(-1,-1),8),('BOTTOMPADDING',(0,0),(-1,-1),8), ('LINEAFTER',(0,0),(1,0),2,colors.white)])) st.append(steps) st.append(sp(10)) st.append(Paragraph('Examples from your coursebook (page 92) / Примеры из вашего учебника (стр. 92)', S('t5', fontName='DV-B', fontSize=11.5, textColor=BLUE))) st.append(sp(4)) ex5 = [ ('The woman ' + hl('sitting', '#3A86FF') + ' in front of you is my cousin.', 'Женщина, сидящая перед вами, моя двоюродная сестра.', '= The woman who is sitting in front of you ...', '= Женщина, которая сидит перед вами ...'), ('Anyone ' + hl('lying', '#3A86FF') + ' will be punished.', 'Любой, кто лжёт, будет наказан.', '= Anyone who lies will be punished.', '= Любой, кто лжёт, будет наказан.'), ('Meals ' + hl('served', '#3A86FF') + ' cold are cheaper.', 'Блюда, подаваемые холодными, дешевле.', '= Meals which are served cold ...', '= Блюда, которые подаются холодными ...'), ('The machine ' + hl('bought', '#3A86FF') + ' at the local shop was faulty.', 'Аппарат, купленный в местном магазине, оказался неисправным.', '= The machine which we bought ...', '= Аппарат, который мы купили ...'), ] for i,(a,b,c_,d) in enumerate(ex5): f = pair(a, b) + pair(c_, d, small=True) st.append(card(f, [L_BLUE, L_PINK, L_YELLOW, L_GREEN][i], [BLUE, PINK, ORANGE, GREEN][i], pad=6)) st.append(sp(6)) f = [Paragraph('<font name="DV-B" color="#FF5D8F">Extra example / Дополнительный пример</font>', st_lab)] f += pair('The students ' + hl('waiting', '#FF5D8F') + ' outside the exam room looked nervous.', 'Студенты, ожидающие у экзаменационной аудитории, выглядели нервными.') f += pair('This is a photo ' + hl('taken', '#FF5D8F') + ' by my grandfather in 1975.', 'Это фотография, сделанная моим дедушкой в 1975 году.') st.append(card(f, L_PINK, PINK)) st.append(PageBreak()) # ================= 6 ================= st.append(banner('6', 'Watch out! Common mistakes', 'Осторожно! Частые ошибки', PURPLE)) st.append(sp(10)) st.append(Paragraph('Mistake 1: the "dangling" participle / Ошибка 1: "висячее" причастие', S('m1', fontName='DV-B', fontSize=11.5, textColor=CORAL))) st.append(sp(4)) st.append(tick(False, hl('Driving to work', '#FF6B6B') + ', a deer ran across the road.', 'Едя на работу, олень пробежал через дорогу. (олень не за рулём!)')) st.append(sp(4)) st.append(tick(True, hl('Driving to work', '#2EC4B6') + ', I saw a deer run across the road.', 'Едя на работу, я увидел, как олень перебежал дорогу.')) st.append(sp(12)) st.append(Paragraph('Mistake 2: wrong voice (-ing or V3?) / Ошибка 2: неверный залог (-ing или V3?)', S('m2', fontName='DV-B', fontSize=11.5, textColor=ORANGE))) st.append(sp(4)) st.append(tick(False, 'The book ' + hl('writing', '#FF6B6B') + ' by Orwell is famous.', 'Книга, пишущая Оруэллом, знаменита. (неверно)')) st.append(sp(4)) st.append(tick(True, 'The book ' + hl('written', '#2EC4B6') + ' by Orwell is famous.', 'Книга, написанная Оруэллом, знаменита.', 'The book does not write. It is written by someone: use V3.', 'Книга не пишет сама. Её кто-то пишет: используйте V3.')) st.append(sp(12)) st.append(Paragraph('Mistake 3: wrong time (simple -ing or having + V3?) / Ошибка 3: неверное время', S('m3', fontName='DV-B', fontSize=11.5, textColor=GREEN))) st.append(sp(4)) st.append(tick(False, hl('Opening', '#FF6B6B') + ' the door, he had already left the house.', 'Открывая дверь, он уже вышел из дома. (неверно по времени)')) st.append(sp(4)) st.append(tick(True, hl('Having opened', '#2EC4B6') + ' the door, he walked into the house.', 'Открыв дверь, он вошёл в дом.', 'Opening and walking in are two steps: first one, then the other.', 'Открытие двери и вход - два шага: сначала один, потом другой.')) st.append(sp(12)) st.append(Paragraph('Mistake 4: forgetting the comma / Ошибка 4: забытая запятая', S('m4', fontName='DV-B', fontSize=11.5, textColor=BLUE))) st.append(sp(4)) f = [] f += pair('When a participle clause comes first, put a comma after it.', 'Если причастный оборот стоит в начале предложения, поставьте после него запятую.') f += pair(hl('Feeling hungry', '#3A86FF') + ', I made a sandwich.', 'Почувствовав голод, я сделал бутерброд.') st.append(card(f, L_BLUE, BLUE)) st.append(sp(8)) f = [Paragraph('<font name="DV-B" color="#8338EC">Style note / Заметка о стиле</font>', st_lab)] f += pair('Participle clauses are more common in writing and formal speech. In casual talk, we often use because, while or and.', 'Причастные обороты чаще встречаются в письменной и формальной речи. В неформальном разговоре мы часто используем because, while или and.') st.append(card(f, L_PURPLE, PURPLE)) st.append(PageBreak()) # ================= 7 ================= st.append(banner('7', 'Daily life examples', 'Примеры из повседневной жизни', PINK)) st.append(sp(10)) daily = [ ('1', 'Morning commute / По дороге на работу', CORAL, L_CORAL, hl('Waiting', '#E63946') + ' for the bus, I scrolled through my phone.', 'Ожидая автобус, я листал ленту в телефоне.', '-ing, same time', '-ing, одновременно'), ('2', 'Oversleeping / Проспал', ORANGE, L_ORANGE, hl('Having missed', '#E07B00') + ' my alarm, I skipped breakfast and ran to work.', 'Проспав будильник, я пропустил завтрак и побежал на работу.', 'having + V3, earlier action', 'having + V3, более раннее действие'), ('3', 'Dinner time / Время ужина', GREEN, L_GREEN, hl('Not wanting', '#17998D') + ' to cook, we ordered a pizza.', 'Не желая готовить, мы заказали пиццу.', 'negative, reason', 'отрицание, причина'), ('4', 'At the shop / В магазине', BLUE, L_BLUE, 'The man ' + hl('standing', '#2563D6') + ' in the queue ahead of me forgot his wallet.', 'Мужчина, стоявший в очереди передо мной, забыл свой кошелёк.', 'with a noun, shortened relative clause', 'с существительным, сокращённое придаточное'), ('5', 'Lunch break / Обеденный перерыв', PURPLE, L_PURPLE, hl('Packed', '#6D28D9') + ' with fresh vegetables, this salad is my favourite lunch.', 'Наполненный свежими овощами, этот салат - мой любимый обед.', 'V3, passive meaning', 'V3, пассивное значение'), ] for (n, ttl, col, lcol, en, ru, tag, tag_ru) in daily: num = Paragraph(f'<para alignment="center"><font name="DV-B" size="22" color="white">{n}</font></para>', st_en) body = [Paragraph(f'<font name="DV-B" size="9" color="{col.hexval().replace("0x","#")}">{ttl}</font>', st_sm)] + pair(en, ru) + \ [Paragraph(f'<font name="DV-B">Type:</font> {tag}', st_sm), Paragraph(f'Тип: {tag_ru}', st_smru)] t = Table([[num, body]], colWidths=[44, W-44]) t.setStyle(TableStyle([('BACKGROUND',(0,0),(0,0),col),('BACKGROUND',(1,0),(1,0),lcol),('VALIGN',(0,0),(0,0),'MIDDLE'),('VALIGN',(1,0),(1,0),'TOP'), ('ROUNDEDCORNERS',[12,12,12,12]),('LEFTPADDING',(1,0),(1,0),10),('TOPPADDING',(0,0),(-1,-1),7),('BOTTOMPADDING',(0,0),(-1,-1),2)])) st.append(t); st.append(sp(9)) st.append(Paragraph('Your turn: tell a partner about your own morning using two of these patterns!', st_en)) st.append(Paragraph('Ваша очередь: расскажите партнёру о своём утре, используя две из этих моделей!', st_ru)) st.append(PageBreak()) # ================= 8 ================= st.append(banner('8', 'Practice time!', 'Время практики!', CORAL)) st.append(sp(8)) st.append(Paragraph('A. Complete with the correct participle form / Дополните правильной формой причастия', S('pa', fontName='DV-B', fontSize=11.5, textColor=CORAL))) st.append(sp(3)) qa = [ ('___ (walk) to work, I saw an old friend.', 'Идя на работу, я увидел старого друга.'), ('___ (finish) the report, she went home.', 'Закончив отчёт, она пошла домой.'), ('The letter ___ (write) in 1990 is still in the drawer.', 'Письмо, написанное в 1990 году, всё ещё лежит в ящике.'), ('___ (not / know) the answer, he stayed silent.', 'Не зная ответа, он промолчал.'), ('The boy ___ (sit) next to me is my cousin.', 'Мальчик, сидящий рядом со мной, мой двоюродный брат.'), ('___ (judge) by the way she was talking, she is obviously from a privileged background.', 'Судя по тому, как она говорила, она, очевидно, из привилегированной семьи.'), ('___ (spend) all day indoors, I really need to get out.', 'Проведя весь день в помещении, я просто обязан выйти на улицу.'), ('___ (leave) to my own devices, I\'d spend all day lounging around at home.', 'Предоставленный сам себе, я бы весь день валялся дома.'), ] f = [] for i,(e,r) in enumerate(qa,1): f += pair(f'<b>{i}.</b> {e}', r, small=True) st.append(card(f, L_CORAL, CORAL, pad=7)) st.append(sp(8)) st.append(Paragraph('B. Shorten the relative clause (coursebook ex. 41) / Сократите придаточное (упр. 41 учебника)', S('pb', fontName='DV-B', fontSize=11.5, textColor=BLUE))) st.append(sp(3)) qb = [ ('The woman who is talking is an actress.', 'Женщина, которая разговаривает, - актриса.'), ('The man who is driving that bus is my brother.', 'Мужчина, который ведёт этот автобус, - мой брат.'), ('The car which was broken into was a Porsche.', 'Машина, в которую взломали, была Порше.'), ('People who are caught stealing are imprisoned.', 'Людей, пойманных на воровстве, сажают в тюрьму.'), ('The woman who lives in this house is Russian.', 'Женщина, которая живёт в этом доме, русская.'), ('The man who is playing the piano is blind.', 'Мужчина, который играет на пианино, слепой.'), ] f = [] for i,(e,r) in enumerate(qb,1): f += pair(f'<b>{i}.</b> {e}', r, small=True) st.append(card(f, L_BLUE, BLUE, pad=7)) st.append(sp(8)) st.append(Paragraph('C. Find and fix the mistake / Найдите и исправьте ошибку', S('pc', fontName='DV-B', fontSize=11.5, textColor=GREEN))) st.append(sp(3)) qc = [ ('Walking down the street, the rain started.', 'Идя по улице, начался дождь.'), ('The book writing by Orwell is famous.', 'Книга, пишущая Оруэллом, знаменита.'), ('Have finished the report, she went home.', 'Закончив отчёт, она пошла домой.'), ] f = [] for i,(e,r) in enumerate(qc,1): f += pair(f'<b>{i}.</b> {e}', r, small=True) st.append(card(f, L_GREEN, GREEN, pad=7)) st.append(PageBreak()) # ================= 9 ================= st.append(banner('9', 'Speaking challenge', 'Разговорное задание', ORANGE)) st.append(sp(8)) f = [] f += pair('Finish each sentence with your own idea. Then compare with a partner. Coursebook tip: say the same start with a different main clause.', 'Закончите каждое предложение своей идеей. Затем сравните с партнёром. Совет из учебника: берите то же начало, но с другим главным предложением.') f += pair('Example: Thinking about it now, I\'ve changed my mind.', 'Пример: Думая об этом сейчас, я передумал.') st.append(card(f, L_ORANGE, ORANGE)) st.append(sp(8)) sp_items = [ ('Thinking about my childhood, ...', 'Думая о своём детстве, ...'), ('Having spent the whole weekend at home, ...', 'Проведя все выходные дома, ...'), ('Not having had breakfast, ...', 'Не позавтракав, ...'), ('Left to my own devices, ...', 'Предоставленный сам себе, ...'), ] f = [] for e,r in sp_items: f += pair(e, r) st.append(card(f, L_YELLOW, YELLOW)) st.append(sp(14)) st.append(banner('10', 'Answer key', 'Ключи к заданиям', BLUE)) st.append(sp(8)) ka = [ '1. Walking 2. Having finished 3. written 4. Not knowing 5. sitting 6. Judging 7. Having spent 8. Left', ] kb = [ '1. The woman talking is an actress.', '2. The man driving that bus is my brother.', '3. The car broken into was a Porsche.', '4. People caught stealing are imprisoned.', '5. The woman living in this house is Russian.', '6. The man playing the piano is blind.'] kb_ru = ['Женщина, говорящая сейчас, - актриса.','Мужчина, ведущий этот автобус, - мой брат.','Взломанная машина была Порше.', 'Люди, пойманные на воровстве, попадают в тюрьму.','Женщина, живущая в этом доме, русская.','Мужчина, играющий на пианино, слепой.'] kc = [ ('Walking down the street, I felt the rain start.', 'Идя по улице, я почувствовал, что начинается дождь.'), ('The book written by Orwell is famous.', 'Книга, написанная Оруэллом, знаменита.'), ('Having finished the report, she went home.', 'Закончив отчёт, она пошла домой.')] f = [Paragraph('<font name="DV-B" color="#FF6B6B">A.</font>', st_lab)] f += pair(ka[0], 'Идя, закончив, написанное, не зная, сидящий, судя, проведя, предоставленный.', small=True) f += [Paragraph('<font name="DV-B" color="#3A86FF">B.</font>', st_lab)] for e, r in zip(kb, kb_ru): f += pair(e, r, small=True) f += [Paragraph('<font name="DV-B" color="#2EC4B6">C.</font>', st_lab)] for e, r in kc: f += pair(f'<b>{e}</b>', r, small=True) st.append(card(f, L_BLUE, BLUE, pad=7)) st.append(PageBreak()) # ================= cheat sheet ================= st.append(banner('11', 'Cheat sheet: remember it forever!', 'Шпаргалка: запомните навсегда!', PURPLE)) st.append(sp(10)) cs = [ (CORAL, L_CORAL, 'Same subject', 'The -ing / V3 action belongs to the subject of the main clause.', 'Действие в обороте принадлежит подлежащему главного предложения.'), (ORANGE, L_ORANGE, 'V-ing = active', 'Reading the menu, she smiled.', 'Читая меню, она улыбнулась.'), (YELLOW, L_YELLOW, 'V3 = passive', 'Surprised by the news, he sat down.', 'Удивлённый новостью, он сел.'), (GREEN, L_GREEN, 'having + V3 = earlier', 'Having locked the door, I left.', 'Заперев дверь, я ушёл.'), (BLUE, L_BLUE, 'not + participle', 'Not feeling well, she stayed at home.', 'Плохо себя чувствуя, она осталась дома.'), (PINK, L_PINK, 'With nouns', 'The girl wearing red is my sister.', 'Девушка в красном - моя сестра.'), (PURPLE, L_PURPLE, 'Comma', 'Put a comma after an opening participle clause.', 'Ставьте запятую после причастного оборота в начале предложения.'), ] for (col, lcol, ttl, en, ru) in cs: head = Paragraph(f'<font name="DV-B" size="10" color="{"#1D3557" if col==YELLOW else "white"}">{ttl}</font>', st_en) t = Table([[head, pair(en, ru, small=True)]], colWidths=[W*0.28, W*0.72]) t.setStyle(TableStyle([('BACKGROUND',(0,0),(0,0),col),('BACKGROUND',(1,0),(1,0),lcol),('VALIGN',(0,0),(0,0),'MIDDLE'), ('ROUNDEDCORNERS',[10,10,10,10]),('LEFTPADDING',(0,0),(-1,-1),9),('TOPPADDING',(0,0),(-1,-1),6),('BOTTOMPADDING',(0,0),(-1,-1),1)])) st.append(t); st.append(sp(7)) st.append(sp(6)) f = [] f += pair('Well done! You can now say more with fewer words, just like a native speaker.', 'Отлично! Теперь вы можете говорить больше, используя меньше слов, как носитель языка.', center=True) f += pair('Keep practising every day: notice participle clauses in articles, films and songs!', 'Продолжайте практиковаться каждый день: замечайте причастные обороты в статьях, фильмах и песнях!', center=True) st.append(card(f, L_PINK, PINK)) doc.build(st) print('done') EOF cd /tmp/workspace/participle-handbook/src && python build.py

Let me render the pages to check the layout.
Running Command

cd /tmp/workspace/participle-handbook && python -c " import fitz d=fitz.open('output/Participle_Clauses_Handbook.pdf');print(len(d)) for i,p in enumerate(d): p.get_pixmap(dpi=60).save(f'output/p{i+1}.png') " 2>&1 | tail -3; ls output

Running Command

cd /tmp/workspace/participle-handbook && python -c " import pypdfium2 as p d=p.PdfDocument('output/Participle_Clauses_Handbook.pdf');print(len(d)) for i in range(len(d)): d[i].render(scale=0.9).to_pil().save(f'output/p{i+1}.png') " 2>&1 | tail -3; ls output

Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Reading File
Running Command

cd /tmp/workspace/participle-handbook/src && python - <<'EOF' s=open('build.py',encoding='utf-8').read() s=s.replace('''<font name="DV-B" size="17" color="{color.hexval().replace("0x","#")}">{num}</font>''','''<font name="DV-B" size="{17 if len(num)==1 else 13}" color="{color.hexval().replace("0x","#")}">{num}</font>''') s=s.replace("""st[-1].setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),ORANGE)""","""st[-1].setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),HexColor('#F4B400'))""") s=s.replace("shows four hidden meanings. Detective time!","shows four hidden meanings (plus one bonus from us). Detective time!") s=s.replace("показаны четыре скрытых значения. Время","показаны четыре скрытых значения (плюс одно бонусное от нас). Время") s=s.replace("Машина, в которую взломали, была Порше.","Машина, которую взломали, была Порше.") s=s.replace("'Идя, закончив, написанное, не зная, сидящий, судя, проведя, предоставленный.'","'1. Идя 2. Закончив 3. написанное 4. Не зная 5. сидящий 6. Судя 7. Проведя 8. Предоставленный'") open('build.py','w',encoding='utf-8').write(s) EOF python build.py && python -c " import pypdfium2 as p d=p.PdfDocument('/tmp/workspace/participle-handbook/output/Participle_Clauses_Handbook.pdf') for i in (3,9): d[i].render(scale=0.7).to_pil().save(f'/tmp/workspace/participle-handbook/output/q{i+1}.png') print(len(d))"

Reading File
Running Command

cd /tmp/workspace/participle-handbook/output && rm -f p*.png q*.png && ls -la

Running Command

cd /tmp/workspace/participle-handbook && python -c " import pypdfium2 as p d=p.PdfDocument('output/Participle_Clauses_Handbook.pdf') d[9].render(scale=0.7).to_pil().save('/tmp/workspace/participle-handbook/src/check10.png') d[2].render(scale=0.5).to_pil().save('/tmp/workspace/participle-handbook/src/check3.png')"; ls src

Reading File
Your handbook is ready. It is an 11-page colourful PDF on participle clauses only, with a Russian translation under every sentence.

Participle Clauses Handbook

PDF Document · PDF

What's inside
  1. Cover and contents: the sentence anatomy, "Walking home, | I met my teacher."
  2. What is a participle clause? A "shortcut machine" shows long and short versions side by side. A "Russian helper" box compares the English forms with причастие and деепричастие.
  3. Meet the forms: a colour-coded table covering V-ing, V3, having + V3, having been + V3, and not + participle.
  4. The Golden Rule: the subject of the participle clause must match the subject of the main clause. It has ✔/✘ examples and a note on keeping joining words (before leaving, while waiting, on arriving).
  5. What can it mean? The four meanings from your Lesson 5B page (result, reason, time, condition), using the coursebook sentences. I added a fifth, contrast, as a bonus and labelled it as extra.
  6. Participle clauses with nouns: a 3-step method plus the four examples from your page 92.
  7. Common mistakes: dangling participles, -ing versus V3, wrong time, and commas.
  8. Five daily-life examples: English with a Russian translation under each, and the type of each one labelled.
  9. Practice, speaking challenge and answer key. It includes your coursebook's exercise 41 and items from exercise 7, plus a one-page cheat sheet.
Things to know
  • The Russian translations are mine. Please have a native speaker or your own judgement check the nuance, especially for the more natural phrasing of constructions like "Having been ignored…".
  • The coursebook's fifth item in exercise 7 ("interested in sports") had no clear answer, so I left it out and wrote my own items instead.
  • The grammar box in your file was cut off after "How do you form the negative?", so the rules on the forms, the same-subject rule and common mistakes come from my own knowledge. I did not search PubMed because this isn't a medical topic.
  • I checked the layout by rendering the pages to images. All pages looked right.

Please recreate the colorful handbook pdf with following changes - firstly write 5 daily life examples in English and under Russian translation for every point of rules and information given for "participle clauses" . Secondly don't write anything about Relative clauses as my main topic is only " participle clauses " only . Thirdly in the end give a worksheet for real practice for my C1 proficiency ESL STUDENT and this worksheet should be really creative and logical effective so that by doing this worksheet my ESL student can really understand participle clauses "in perfect way.

Writing File

~/participle-handbook/src/build2.py

# -*- coding: utf-8 -*-
import os, re, matplotlib
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.colors import HexColor
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_CENTER
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import (BaseDocTemplate, PageTemplate, Frame, Paragraph, Spacer, Table,
                                TableStyle, PageBreak, NextPageTemplate, KeepTogether, HRFlowable)

fd = os.path.join(os.path.dirname(matplotlib.__file__), 'mpl-data/fonts/ttf')
for n, f in [('DV','DejaVuSans.ttf'),('DV-B','DejaVuSans-Bold.ttf'),('DV-I','DejaVuSans-Oblique.ttf'),('DV-BI','DejaVuSans-BoldOblique.ttf')]:
    pdfmetrics.registerFont(TTFont(n, os.path.join(fd, f)))
pdfmetrics.registerFontFamily('DV', normal='DV', bold='DV-B', italic='DV-I', boldItalic='DV-BI')

CORAL=HexColor('#FF6B6B'); ORANGE=HexColor('#FF9F1C'); GOLD=HexColor('#F4B400'); GREEN=HexColor('#2EC4B6')
BLUE=HexColor('#3A86FF'); PURPLE=HexColor('#8338EC'); PINK=HexColor('#FF5D8F'); NAVY=HexColor('#1D3557'); YELLOW=HexColor('#FFD93D')
L_CORAL=HexColor('#FFE3E3'); L_ORANGE=HexColor('#FFF0D6'); L_GOLD=HexColor('#FFF6D0'); L_GREEN=HexColor('#D9F7F3')
L_BLUE=HexColor('#DEEBFF'); L_PURPLE=HexColor('#EBDDFF'); L_PINK=HexColor('#FFE0EB'); CREAM=HexColor('#FFFBF0')
RUC='#6A3FB5'
DARK = {id(CORAL):'#D62839', id(ORANGE):'#C26A00', id(GOLD):'#9A6B00', id(GREEN):'#0B8A7D', id(BLUE):'#1F5FD6', id(PURPLE):'#6D28D9', id(PINK):'#D6195E'}
PW, PH = A4
M = 40
W = PW - 2*M

def S(name, **kw):
    b = dict(fontName='DV', fontSize=10, leading=14, textColor=NAVY); b.update(kw); return ParagraphStyle(name, **b)
st_en  = S('en', fontSize=10, leading=14)
st_ru  = S('ru', fontName='DV-I', fontSize=8.8, leading=12.2, textColor=HexColor(RUC), spaceAfter=4)
st_rule= S('rule', fontName='DV-B', fontSize=10, leading=14)
st_ruru= S('ruru', fontName='DV-I', fontSize=8.8, leading=12.2, textColor=HexColor(RUC), spaceAfter=4)
st_sm  = S('sm', fontSize=9.2, leading=12.5)
st_smru= S('smru', fontName='DV-I', fontSize=8.3, leading=11.5, textColor=HexColor(RUC), spaceAfter=3)
st_h   = S('h', fontName='DV-B', fontSize=16, leading=20, textColor=colors.white)
st_hru = S('hru', fontName='DV-I', fontSize=10, leading=13, textColor=colors.white)
st_lab = S('lab', fontName='DV-B', fontSize=11, leading=14)
st_c   = S('c', fontSize=10, leading=14, alignment=TA_CENTER)
st_cru = S('cru', fontName='DV-I', fontSize=8.8, leading=12, textColor=HexColor(RUC), alignment=TA_CENTER, spaceAfter=3)

def mk(s, col):
    d = DARK.get(id(col), '#D62839')
    return re.sub(r'\{\{(.+?)\}\}', lambda m: f'<font color="{d}"><b>{m.group(1)}</b></font>', s)

def pair(en, ru, rule=False, small=False, center=False):
    if center: return [Paragraph(en, st_c), Paragraph(ru, st_cru)]
    if small: return [Paragraph(en, st_sm), Paragraph(ru, st_smru)]
    if rule: return [Paragraph(en, st_rule), Paragraph(ru, st_ruru)]
    return [Paragraph(en, st_en), Paragraph(ru, st_ru)]

def sp(h=8): return Spacer(1, h)

def card(flows, bg, border, width=W, pad=9):
    t = Table([[flows]], colWidths=[width])
    t.setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),bg),('BOX',(0,0),(-1,-1),1.5,border),('ROUNDEDCORNERS',[10,10,10,10]),
        ('LEFTPADDING',(0,0),(-1,-1),pad+2),('RIGHTPADDING',(0,0),(-1,-1),pad+2),('TOPPADDING',(0,0),(-1,-1),pad),('BOTTOMPADDING',(0,0),(-1,-1),pad-2)]))
    return t

def banner(num, title, ru, color):
    hexc = DARK.get(id(color), '#333333')
    fs = 17 if len(num) == 1 else 12
    left = Table([[Paragraph(f'<para alignment="center"><font name="DV-B" size="{fs}" color="{hexc}">{num}</font></para>', st_en)]], colWidths=[36], rowHeights=[36])
    left.setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),colors.white),('ROUNDEDCORNERS',[18,18,18,18]),('VALIGN',(0,0),(-1,-1),'MIDDLE'),
                              ('LEFTPADDING',(0,0),(-1,-1),0),('RIGHTPADDING',(0,0),(-1,-1),0)]))
    t = Table([[left, [Paragraph(title, st_h), Paragraph(ru, st_hru)]]], colWidths=[50, W-50])
    t.setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),color),('ROUNDEDCORNERS',[12,12,12,12]),('VALIGN',(0,0),(-1,-1),'MIDDLE'),
        ('LEFTPADDING',(0,0),(0,0),8),('TOPPADDING',(0,0),(-1,-1),8),('BOTTOMPADDING',(0,0),(-1,-1),8)]))
    return t

def point(code, title, ru_title, rules, exs, col, lcol, pairs=False):
    """rules: list of (en, ru); exs: list of (en, ru) or (wrong, wrong_ru, right, right_ru)"""
    head = [Paragraph(f'<font name="DV-B" size="10.5" color="white">{code}  {title}</font><br/><font name="DV-I" size="8.5" color="white">{ru_title}</font>', st_en)]
    body = []
    for e, r in rules: body += pair(mk(e, col), r, rule=True)
    lab = '5 daily life examples / 5 примеров из повседневной жизни' if not pairs else '5 mistakes and fixes / 5 ошибок и исправлений'
    body.append(Paragraph(f'<font name="DV-B" size="8.5" color="{DARK.get(id(col))}">{lab}</font>', st_en))
    body.append(Spacer(1, 3))
    for i, ex in enumerate(exs, 1):
        if not pairs:
            e, r = ex
            body += [Paragraph(f'<font name="DV-B" color="{DARK.get(id(col))}">{i}.</font> ' + mk(e, col), st_sm), Paragraph(r, st_smru)]
        else:
            we, wr, re_, rr = ex
            body += [Paragraph(f'<font name="DV-B" color="#D62839">{i}. &#10008;</font> ' + we, st_sm), Paragraph(wr, st_smru),
                     Paragraph('<font name="DV-B" color="#0B8A7D">&#10004;</font> ' + mk(re_, GREEN), st_sm), Paragraph(rr, st_smru)]
    t = Table([[head], [body]], colWidths=[W])
    t.setStyle(TableStyle([('BACKGROUND',(0,0),(0,0),col),('BACKGROUND',(0,1),(0,1),lcol),('BOX',(0,0),(-1,-1),1.3,col),
        ('ROUNDEDCORNERS',[10,10,10,10]),('LEFTPADDING',(0,0),(-1,-1),11),('RIGHTPADDING',(0,0),(-1,-1),11),
        ('TOPPADDING',(0,0),(0,0),5),('BOTTOMPADDING',(0,0),(0,0),5),('TOPPADDING',(0,1),(0,1),7),('BOTTOMPADDING',(0,1),(0,1),3)]))
    return KeepTogether([t, sp(9)])

# ---------- page decorations ----------
BANDS = [CORAL, ORANGE, YELLOW, GREEN, BLUE, PURPLE, PINK]
def content_page(c, doc):
    c.saveState()
    bw = PW/len(BANDS)
    for i, b in enumerate(BANDS):
        c.setFillColor(b); c.rect(i*bw, PH-14, bw, 14, stroke=0, fill=1)
    c.setFont('DV-B', 8); c.setFillColor(NAVY); c.drawString(M, PH-30, 'PARTICIPLE CLAUSES')
    c.setFont('DV-I', 8); c.setFillColor(HexColor(RUC)); c.drawRightString(PW-M, PH-30, 'Причастные обороты  |  C1 Handbook')
    for i, b in enumerate(BANDS):
        c.setFillColor(b); c.circle(PW/2 - 36 + i*12, 22, 4, stroke=0, fill=1)
    c.setFont('DV-B', 9); c.setFillColor(NAVY); c.drawRightString(PW-M, 18, str(doc.page))
    c.restoreState()

def cover_page(c, doc):
    c.saveState()
    c.setFillColor(CREAM); c.rect(0,0,PW,PH,stroke=0,fill=1)
    c.setFillColor(PURPLE); c.roundRect(-20, 470, PW+40, 400, 40, stroke=0, fill=1)
    for (x,y,r,col) in [(60,790,38,CORAL),(540,800,30,YELLOW),(500,520,45,PINK),(90,500,26,GREEN),(300,820,20,ORANGE),(560,640,18,GREEN),(40,640,16,YELLOW)]:
        c.setFillColor(col); c.circle(x,y,r,stroke=0,fill=1)
    c.setFillColor(colors.white); c.setFont('DV-B', 13); c.drawCentredString(PW/2, 740, 'A COLOURFUL HANDBOOK  |  C1 ESL')
    c.setFont('DV-B', 50); c.drawCentredString(PW/2, 670, 'PARTICIPLE')
    c.setFillColor(YELLOW); c.drawCentredString(PW/2, 610, 'CLAUSES')
    c.setFillColor(colors.white); c.setFont('DV-I', 17); c.drawCentredString(PW/2, 568, 'Причастные обороты')
    c.setFont('DV', 10.5); c.drawCentredString(PW/2, 530, 'Say more with fewer words: rules, examples and a real practice worksheet!')
    c.setFont('DV-I', 9.5); c.drawCentredString(PW/2, 514, 'Говорите больше меньшим числом слов: правила, примеры и настоящий рабочий лист!')
    y = 395
    c.setFont('DV-B', 12); c.setFillColor(NAVY); c.drawCentredString(PW/2, y+55, 'Sentence anatomy / Строение предложения')
    c.setFillColor(CORAL); c.roundRect(52, y, 230, 42, 12, stroke=0, fill=1)
    c.setFillColor(GREEN); c.roundRect(300, y, 243, 42, 12, stroke=0, fill=1)
    c.setFillColor(colors.white); c.setFont('DV-B', 13)
    c.drawCentredString(167, y+16, 'Walking home,'); c.drawCentredString(421, y+16, 'I met my teacher.')
    c.setFont('DV-B', 8.5); c.setFillColor(NAVY)
    c.drawCentredString(167, y-14, 'PARTICIPLE CLAUSE'); c.drawCentredString(421, y-14, 'MAIN CLAUSE')
    c.setFont('DV-I', 8.5); c.setFillColor(HexColor(RUC))
    c.drawCentredString(167, y-27, 'причастный оборот'); c.drawCentredString(421, y-27, 'главное предложение')
    c.setFont('DV-I', 10); c.drawCentredString(PW/2, y-52, 'Идя домой, я встретил своего учителя.')
    chips = [('1','What is it?',CORAL),('2','The forms',ORANGE),('3','Golden rule',GOLD),('4','Joining words',GREEN),
             ('5','Meanings',BLUE),('6','Position, style',PURPLE),('7','Mistakes',PINK),('8','Worksheet',CORAL)]
    cy = 255
    c.setFont('DV-B', 11); c.setFillColor(NAVY); c.drawCentredString(PW/2, cy+38, 'Inside this handbook / Что внутри')
    for i,(n,t,col) in enumerate(chips):
        row, cm = divmod(i,4); x = 52 + cm*125; yy = cy - row*44
        c.setFillColor(col); c.roundRect(x, yy, 118, 34, 10, stroke=0, fill=1)
        c.setFillColor(colors.white); c.setFont('DV-B', 11); c.drawString(x+10, yy+12, n); c.setFont('DV-B', 9.5); c.drawString(x+26, yy+12, t)
    c.setFont('DV-I', 8.5); c.setFillColor(HexColor(RUC))
    c.drawCentredString(PW/2, 125, 'Based on your coursebook (Lesson 5B: participle clauses) plus extra explanations.')
    c.drawCentredString(PW/2, 112, 'Основано на вашем учебнике (урок 5B: причастные обороты) и дополнено объяснениями.')
    c.restoreState()

OUT = '/tmp/workspace/participle-handbook/output/Participle_Clauses_Handbook_v2.pdf'
doc = BaseDocTemplate(OUT, pagesize=A4, leftMargin=M, rightMargin=M, topMargin=48, bottomMargin=40,
        title='Participle Clauses: A Colourful C1 Handbook and Worksheet', author='Orris')
doc.addPageTemplates([
    PageTemplate(id='cover', frames=[Frame(0,0,1,1,id='x')], onPage=cover_page),
    PageTemplate(id='content', frames=[Frame(M, 40, W, PH-48-40, id='f', leftPadding=0, rightPadding=0, topPadding=0, bottomPadding=0)], onPage=content_page)])

story = [NextPageTemplate('content'), PageBreak()]
def part(num, title, ru, col, intro=None):
    story.append(banner(num, title, ru, col)); story.append(sp(9))
    if intro: story.append(card(pair(*intro, rule=False), HexColor('#FFFFFF'), col, pad=6)); story.append(sp(9))

# ============ PART 1 ============
part('1', 'What is a participle clause?', 'Что такое причастный оборот?', CORAL)
story.append(point('1.1', 'The big idea', 'Главная идея', [
    ('A participle clause is a short clause that begins with an {{-ing form}} or a {{past participle}}.',
     'Причастный оборот - это короткая часть предложения, которая начинается с формы -ing или с причастия прошедшего времени.'),
    ('It adds extra information to the main clause, so we say more with fewer words.',
     'Он добавляет дополнительную информацию к главному предложению, поэтому мы говорим больше меньшим числом слов.')], [
    ('{{Opening the fridge}}, I realised we had no milk.', 'Открыв холодильник, я понял, что молока нет.'),
    ('{{Feeling hungry}}, she made a sandwich.', 'Почувствовав голод, она сделала бутерброд.'),
    ('{{Stuck in traffic}}, we missed the start of the film.', 'Застряв в пробке, мы пропустили начало фильма.'),
    ('{{Having finished my emails}}, I closed the laptop.', 'Закончив письма, я закрыл ноутбук.'),
    ('{{Not knowing the way}}, he asked a passer-by.', 'Не зная дороги, он спросил прохожего.')], CORAL, L_CORAL))
story.append(point('1.2', 'The main clause gives the subject and the tense', 'Главное предложение задаёт подлежащее и время', [
    ('The verb in the main clause shows the time. The participle clause has no tense of its own.',
     'Глагол в главном предложении показывает время. У причастного оборота нет собственного времени.')], [
    ('{{Looking at my phone}}, I walked into a door. (past)', 'Глядя в телефон, я врезался в дверь. (прошедшее)'),
    ('{{Looking at the menu}}, she always orders the same thing. (present)', 'Глядя в меню, она всегда заказывает одно и то же. (настоящее)'),
    ('{{Waiting for the lift}}, I will check my messages. (future)', 'Ожидая лифт, я проверю сообщения. (будущее)'),
    ('{{Having cleaned the kitchen}}, she is now relaxing on the sofa. (present)', 'Убрав кухню, она сейчас отдыхает на диване. (настоящее)'),
    ('{{Sitting in the garden}}, they were drinking tea. (past continuous)', 'Сидя в саду, они пили чай. (прошедшее длительное)')], CORAL, L_CORAL))
story.append(point('1.3', 'Russian helper', 'Подсказка для русскоговорящих', [
    ('English -ing often matches a Russian деепричастие, and V3 often matches a passive participle.',
     'Английское -ing часто соответствует русскому деепричастию, а V3 часто соответствует страдательному причастию.'),
    ('Good news: you already know this idea from Russian!', 'Хорошая новость: эта идея вам уже знакома по русскому языку!')], [
    ('{{Walking}} to work, I listen to podcasts. (= идя)', 'Идя на работу, я слушаю подкасты.'),
    ('{{Having walked}} ten kilometres, we were exhausted. (= пройдя)', 'Пройдя десять километров, мы были измотаны.'),
    ('{{Not walking}} fast enough, he missed the bus. (= не идя)', 'Недостаточно быстро идя, он опоздал на автобус.'),
    ('{{Dressed}} in black, she looked elegant. (= одетая)', 'Одетая в чёрное, она выглядела элегантно.'),
    ('{{Written}} in a hurry, the email had mistakes. (= написанное)', 'Написанное в спешке, письмо содержало ошибки.')], CORAL, L_CORAL))

# ============ PART 2 ============
story.append(PageBreak())
part('2', 'Meet the forms', 'Знакомьтесь с формами', ORANGE)
story.append(point('2.1', 'V-ing: the subject does the action', 'V-ing: подлежащее само выполняет действие', [
    ('Use V-ing when the subject is doing the action. It usually happens at the same time as the main action.',
     'Используйте V-ing, когда подлежащее само выполняет действие. Обычно оно происходит одновременно с главным действием.')], [
    ('{{Brushing my teeth}}, I check my calendar.', 'Чистя зубы, я просматриваю свой календарь.'),
    ('{{Cooking dinner}}, he burnt his finger.', 'Готовя ужин, он обжёг палец.'),
    ('{{Crossing the road}}, she dropped her keys.', 'Переходя дорогу, она уронила ключи.'),
    ('{{Sipping her coffee}}, Anna read the news.', 'Потягивая кофе, Анна читала новости.'),
    ('{{Listening to music}}, I didn\'t hear the doorbell.', 'Слушая музыку, я не услышал дверной звонок.')], ORANGE, L_ORANGE))
story.append(point('2.2', 'V3: something is done to the subject', 'V3: что-то делают с подлежащим', [
    ('Use the past participle when the meaning is passive. It often describes a state or a feeling.',
     'Используйте причастие прошедшего времени, когда значение страдательное. Оно часто описывает состояние или чувство.')], [
    ('{{Exhausted after work}}, he fell asleep on the sofa.', 'Измотанный после работы, он заснул на диване.'),
    ('{{Left alone at home}}, the dog started barking.', 'Оставшись один дома, пёс начал лаять.'),
    ('{{Surprised by the price}}, she put the jacket back.', 'Удивлённая ценой, она положила куртку обратно.'),
    ('{{Covered in mud}}, the kids ran into the kitchen.', 'Покрытые грязью, дети вбежали на кухню.'),
    ('{{Dressed in a hurry}}, I put my shirt on inside out.', 'Одевшись впопыхах, я надел рубашку наизнанку.')], ORANGE, L_ORANGE))
story.append(point('2.3', 'having + V3: the earlier action', 'having + V3: более раннее действие', [
    ('Use having + V3 when the participle action finished BEFORE the main action.',
     'Используйте having + V3, когда действие причастного оборота закончилось РАНЬШЕ главного действия.')], [
    ('{{Having locked the door}}, I left for work.', 'Заперев дверь, я ушёл на работу.'),
    ('{{Having eaten too much cake}}, he felt sick.', 'Объевшись торта, он почувствовал тошноту.'),
    ('{{Having missed the bus}}, she took a taxi.', 'Опоздав на автобус, она взяла такси.'),
    ('{{Having read the reviews}}, we booked the hotel.', 'Прочитав отзывы, мы забронировали отель.'),
    ('{{Having saved enough money}}, they bought a car.', 'Накопив достаточно денег, они купили машину.')], ORANGE, L_ORANGE))
story.append(point('2.4', 'having been + V3: earlier and passive', 'having been + V3: раньше и в страдательном залоге', [
    ('Use having been + V3 for an earlier action that happened to the subject.',
     'Используйте having been + V3 для более раннего действия, которое произошло с подлежащим.')], [
    ('{{Having been stuck in a lift for an hour}}, she took the stairs from then on.', 'Просидев час в застрявшем лифте, она с тех пор ходила по лестнице.'),
    ('{{Having been told the shop was closed}}, we went home.', 'Узнав, что магазин закрыт, мы пошли домой.'),
    ('{{Having been woken by the noise}}, I couldn\'t get back to sleep.', 'Разбуженный шумом, я не смог снова уснуть.'),
    ('{{Having been given a discount}}, he bought two pairs.', 'Получив скидку, он купил две пары.'),
    ('{{Having been warned about the weather}}, they took umbrellas.', 'Предупреждённые о погоде, они взяли зонты.')], ORANGE, L_ORANGE))
story.append(point('2.5', 'The negative: not goes first', 'Отрицание: not ставится первым', [
    ('Put not (or never) directly before the participle. For an earlier negative action use not having + V3.',
     'Ставьте not (или never) прямо перед причастием. Для более раннего отрицательного действия используйте not having + V3.')], [
    ('{{Not wanting to cook}}, we ordered pizza.', 'Не желая готовить, мы заказали пиццу.'),
    ('{{Not having a charger}}, I couldn\'t use my phone.', 'Не имея зарядки, я не мог пользоваться телефоном.'),
    ('{{Not feeling well}}, she stayed in bed.', 'Плохо себя чувствуя, она осталась в постели.'),
    ('{{Never having driven in snow}}, he drove very slowly.', 'Никогда не ездив по снегу, он ехал очень медленно.'),
    ('{{Not being a morning person}}, I hate early meetings.', 'Не будучи жаворонком, я ненавижу ранние встречи.')], ORANGE, L_ORANGE))
story.append(point('2.6', 'Being: reason or state', 'Being: причина или состояние', [
    ('Being + adjective or noun gives a reason or a state. In many cases, being can be dropped: (Being) tired, ...',
     'Being + прилагательное или существительное даёт причину или состояние. Часто being можно опустить: (Being) tired, ...')], [
    ('{{Being late}}, I took a taxi.', 'Опаздывая, я взял такси.'),
    ('{{(Being) tired}}, she went to bed early.', 'Устав, она рано легла спать.'),
    ('{{Being a student}}, he gets a discount on trains.', 'Будучи студентом, он получает скидку на поезда.'),
    ('{{(Being) unable to sleep}}, I made some tea.', 'Не имея возможности уснуть, я заварил чай.'),
    ('{{Being the youngest}}, she always gets the last piece of cake.', 'Будучи самой младшей, она всегда получает последний кусочек торта.')], ORANGE, L_ORANGE))
story.append(point('2.7', 'Time travel check: -ing or having + V3?', 'Проверка по времени: -ing или having + V3?', [
    ('V-ing = the actions happen at the same time. Having + V3 = first this, then that.',
     'V-ing = действия происходят одновременно. Having + V3 = сначала одно, потом другое.')], [
    ('{{Walking to the station}}, I called Mum. (same time)', 'Идя на станцию, я позвонил маме. (одновременно)'),
    ('{{Having reached the station}}, I called Mum. (first, then)', 'Дойдя до станции, я позвонил маме. (сначала, потом)'),
    ('{{Eating breakfast}}, he watched the news. (same time)', 'Завтракая, он смотрел новости. (одновременно)'),
    ('{{Having eaten breakfast}}, he left for work. (first, then)', 'Позавтракав, он ушёл на работу. (сначала, потом)'),
    ('{{Looking out of the window}}, she saw a fox. (same time)', 'Глядя в окно, она увидела лису. (одновременно)')], ORANGE, L_ORANGE))

# ============ PART 3 ============
story.append(PageBreak())
part('3', 'The Golden Rule: who does the action?', 'Золотое правило: кто выполняет действие?', GOLD)
story.append(point('3.1', 'Same subject', 'Одно и то же подлежащее', [
    ('The person doing the action in the participle clause must be the SUBJECT of the main clause.',
     'Тот, кто выполняет действие в причастном обороте, должен быть ПОДЛЕЖАЩИМ главного предложения.'),
    ('Russian has the same rule for the деепричастие.', 'В русском такое же правило действует для деепричастия.')], [
    ('{{Opening the door}}, I found a parcel.', 'Открыв дверь, я нашёл посылку.'),
    ('{{Waiting at the dentist\'s}}, she read a magazine.', 'Ожидая приёма у стоматолога, она читала журнал.'),
    ('{{Running for the train}}, he dropped his wallet.', 'Бежа на поезд, он выронил кошелёк.'),
    ('{{Checking my bank account}}, I noticed a strange payment.', 'Проверяя банковский счёт, я заметил странный платёж.'),
    ('{{Carrying three bags}}, Tom couldn\'t open the door.', 'Неся три сумки, Том не мог открыть дверь.')], GOLD, L_GOLD))
story.append(point('3.2', 'How to fix a "dangling" participle', 'Как исправить "висячее" причастие', [
    ('If the subjects are different, the sentence sounds silly. Change the main clause so that the subject matches.',
     'Если подлежащие разные, предложение звучит нелепо. Измените главное предложение, чтобы подлежащее совпало.')], [
    ('Driving to work, a deer ran across the road.', 'Едя на работу, олень перебежал дорогу. (олень за рулём?)',
     '{{Driving to work}}, I saw a deer run across the road.', 'Едя на работу, я увидел, как олень перебежал дорогу.'),
    ('Cooking dinner, the phone rang.', 'Готовя ужин, зазвонил телефон. (телефон готовит?)',
     '{{Cooking dinner}}, I heard the phone ring.', 'Готовя ужин, я услышал звонок телефона.'),
    ('Walking down the stairs, my phone fell.', 'Спускаясь по лестнице, мой телефон упал. (телефон спускается?)',
     '{{Walking down the stairs}}, I dropped my phone.', 'Спускаясь по лестнице, я уронил телефон.'),
    ('Reading the menu, the waiter arrived.', 'Читая меню, подошёл официант. (официант читает?)',
     '{{Reading the menu}}, we saw the waiter arrive.', 'Читая меню, мы увидели, что подошёл официант.'),
    ('Opening the fridge, the milk smelled terrible.', 'Открыв холодильник, молоко ужасно пахло. (молоко открывает?)',
     '{{Opening the fridge}}, I noticed the milk smelled terrible.', 'Открыв холодильник, я заметил, что молоко ужасно пахнет.')], GOLD, L_GOLD, pairs=True))
story.append(point('3.3', 'Fixed phrases can break the rule', 'Устойчивые выражения могут нарушать правило', [
    ('Some fixed phrases (judging by, generally speaking, considering) work with any subject because they comment on the whole sentence.',
     'Некоторые устойчивые выражения (judging by, generally speaking, considering) работают с любым подлежащим, потому что они комментируют всё предложение.')], [
    ('{{Judging by the queue}}, the café must be popular.', 'Судя по очереди, это кафе, должно быть, популярно.'),
    ('{{Generally speaking}}, trains here are on time.', 'Вообще говоря, поезда здесь ходят вовремя.'),
    ('{{Strictly speaking}}, you shouldn\'t park here.', 'Строго говоря, здесь нельзя парковаться.'),
    ('{{Considering the weather}}, the picnic went well.', 'Учитывая погоду, пикник прошёл хорошо.'),
    ('{{Speaking of food}}, are you hungry?', 'Кстати о еде, ты не проголодался?')], GOLD, L_GOLD))
story.append(point('3.4', 'Bonus: the clause with its own subject', 'Бонус: оборот со своим подлежащим', [
    ('In a more formal style, a participle clause can have its own subject (an absolute clause). This is a C1 bonus.',
     'В более формальном стиле у причастного оборота может быть своё подлежащее (абсолютная конструкция). Это бонус для уровня C1.')], [
    ('{{Weather permitting}}, we\'ll have a barbecue on Sunday.', 'Если погода позволит, в воскресенье мы устроим барбекю.'),
    ('{{The traffic being heavy}}, we decided to walk.', 'Так как движение было плотным, мы решили идти пешком.'),
    ('{{All the tickets sold}}, the concert was moved to a bigger hall.', 'Так как все билеты были проданы, концерт перенесли в зал побольше.'),
    ('{{The kids asleep}}, we finally had a quiet evening.', 'Когда дети уснули, у нас наконец был тихий вечер.'),
    ('{{Dinner finished}}, everyone moved to the living room.', 'Когда ужин закончился, все перешли в гостиную.')], GOLD, L_GOLD))

# ============ PART 4 ============
story.append(PageBreak())
part('4', 'Joining words that can stay', 'Союзы, которые можно оставить', GREEN)
story.append(point('4.1', 'After, before, while, since, on', 'After, before, while, since, on', [
    ('These words can stay in front of the -ing form to make the time relationship clearer.',
     'Эти слова можно оставить перед формой -ing, чтобы связь по времени была яснее.')], [
    ('{{Before leaving}}, check that the lights are off.', 'Перед уходом проверьте, что свет выключен.'),
    ('{{While waiting}} for my coffee, I answered three emails.', 'Пока я ждал кофе, я ответил на три письма.'),
    ('{{After finishing}} the report, she went for a run.', 'Закончив отчёт, она пошла на пробежку.'),
    ('{{Since moving}} to London, he has made lots of friends.', 'С тех пор как он переехал в Лондон, он завёл много друзей.'),
    ('{{On arriving}} at the hotel, we called our parents.', 'По прибытии в отель мы позвонили родителям.')], GREEN, L_GREEN))
story.append(point('4.2', 'Although, if, unless, once, when', 'Although, if, unless, once, when', [
    ('These words can stay before -ing, V3 or being to show contrast, condition or time.',
     'Эти слова можно оставить перед -ing, V3 или being, чтобы показать уступку, условие или время.')], [
    ('{{Although tired}}, she went to the gym.', 'Хотя она устала, она пошла в спортзал.'),
    ('{{If left in the sun}}, chocolate melts quickly.', 'Если оставить шоколад на солнце, он быстро тает.'),
    ('{{Once opened}}, the milk should be kept in the fridge.', 'После вскрытия молоко нужно хранить в холодильнике.'),
    ('{{Unless told otherwise}}, we\'ll meet at six.', 'Если нам не скажут иначе, мы встретимся в шесть.'),
    ('{{When asked about the price}}, the seller smiled.', 'Когда его спросили о цене, продавец улыбнулся.')], GREEN, L_GREEN))

# ============ PART 5 ============
part('5', 'What can it mean?', 'Что может означать причастный оборот?', BLUE,
     intro=('The participle clause alone does not tell you the meaning. The context decides. Try replacing it with because, while, so, if or although.',
            'Сам по себе причастный оборот не говорит о значении. Это определяет контекст. Попробуйте заменить его на because, while, so, if или although.'))
story.append(point('5.1', 'Reason (= because / as / since)', 'Причина (= потому что / так как)', [
    ('The participle clause explains WHY the main action happened.', 'Причастный оборот объясняет, ПОЧЕМУ произошло главное действие.')], [
    ('{{Feeling dizzy}}, he sat down.', 'Почувствовав головокружение, он сел.'),
    ('{{Not having a car}}, she cycles everywhere.', 'Поскольку у неё нет машины, она ездит везде на велосипеде.'),
    ('{{Being short of time}}, we ordered takeaway.', 'Из-за нехватки времени мы заказали еду на вынос.'),
    ('{{Having lost my keys}}, I called a locksmith.', 'Потеряв ключи, я вызвал слесаря.'),
    ('{{Worried about the exam}}, Maria couldn\'t sleep.', 'Беспокоясь об экзамене, Мария не могла уснуть.')], BLUE, L_BLUE))
story.append(point('5.2', 'Time (= while / when / after)', 'Время (= пока / когда / после того как)', [
    ('The participle clause shows WHEN the main action happened, at the same time or just before.',
     'Причастный оборот показывает, КОГДА произошло главное действие: одновременно или чуть раньше.')], [
    ('{{Sitting on the bus}}, I watched people outside.', 'Сидя в автобусе, я наблюдал за людьми на улице.'),
    ('{{Taking the kids to school}}, Dad listens to the radio.', 'Отвозя детей в школу, папа слушает радио.'),
    ('{{Having had lunch}}, we went for a walk.', 'Пообедав, мы пошли гулять.'),
    ('{{Arriving home}}, I kicked off my shoes.', 'Придя домой, я скинул обувь.'),
    ('{{Washing up}}, she sang her favourite song.', 'Моя посуду, она пела свою любимую песню.')], BLUE, L_BLUE))
story.append(point('5.3', 'Result (= so / which meant)', 'Результат (= поэтому / что означало)', [
    ('A V-ing clause AFTER the main clause often shows the result of the main action. Use a comma before it.',
     'Оборот с V-ing ПОСЛЕ главного предложения часто показывает результат главного действия. Перед ним ставится запятая.')], [
    ('The train was cancelled, {{leaving hundreds of people stranded}}.', 'Поезд отменили, и сотни людей остались на вокзале.'),
    ('The printer jammed, {{making us late for the meeting}}.', 'Принтер замяло бумагой, из-за чего мы опоздали на совещание.'),
    ('He dropped his phone, {{cracking the screen}}.', 'Он уронил телефон, и экран треснул.'),
    ('It rained all weekend, {{ruining our camping trip}}.', 'Все выходные шёл дождь, испортив нам поход.'),
    ('The shop closed early, {{leaving me without any bread}}.', 'Магазин закрылся рано, и я остался без хлеба.')], BLUE, L_BLUE))
story.append(point('5.4', 'Condition (= if / when)', 'Условие (= если / когда)', [
    ('The participle clause sets a condition for the main clause. V3 is very common here.',
     'Причастный оборот задаёт условие для главного предложения. Здесь очень часто используется V3.')], [
    ('{{Faced with a long queue}}, I\'d probably go home.', 'Столкнувшись с длинной очередью, я бы, наверное, ушёл домой.'),
    ('{{Left in the fridge}}, the cake will stay fresh for days.', 'Если оставить торт в холодильнике, он останется свежим несколько дней.'),
    ('{{Used regularly}}, this app improves your English.', 'При регулярном использовании это приложение улучшает ваш английский.'),
    ('{{Taken before meals}}, the tablets work better.', 'Если принимать таблетки перед едой, они действуют лучше.'),
    ('{{Turning left at the lights}}, you\'ll see the post office.', 'Повернув налево у светофора, вы увидите почту.')], BLUE, L_BLUE))
story.append(point('5.5', 'Contrast (= although)', 'Уступка (= хотя)', [
    ('The participle clause shows something unexpected. The context (often still or yet) tells us it is contrast.',
     'Причастный оборот показывает нечто неожиданное. Контекст (часто still или yet) подсказывает, что это уступка.')], [
    ('{{Having studied for hours}}, he still failed the quiz.', 'Хотя он учился несколько часов, он всё равно не сдал тест.'),
    ('{{Being very tired}}, she stayed up to finish the film.', 'Хотя она очень устала, она не ложилась, чтобы досмотреть фильм.'),
    ('{{Knowing it was unhealthy}}, he still ate fast food every day.', 'Зная, что это вредно, он всё равно ел фастфуд каждый день.'),
    ('{{Working from home}}, I still manage to be late for meetings.', 'Работая из дома, я всё равно умудряюсь опаздывать на встречи.'),
    ('{{Having slept for ten hours}}, I still felt tired.', 'Проспав десять часов, я всё равно чувствовал усталость.')], BLUE, L_BLUE))

# ============ PART 6 ============
story.append(PageBreak())
part('6', 'Position and style', 'Положение в предложении и стиль', PURPLE)
story.append(point('6.1', 'Where do we put it? Commas', 'Где его ставить? Запятые', [
    ('A participle clause can come at the start, in the middle or at the end. Separate it with a comma, except after some short, closely linked verbs.',
     'Причастный оборот может стоять в начале, в середине или в конце. Отделяйте его запятой, кроме случаев после некоторых коротких, тесно связанных глаголов.')], [
    ('{{Feeling sick}}, I went home. (start)', 'Почувствовав себя плохо, я пошёл домой. (в начале)'),
    ('I went home, {{feeling sick}}. (end)', 'Я пошёл домой, чувствуя себя плохо. (в конце)'),
    ('My neighbour, {{hearing the noise}}, called the police. (middle)', 'Мой сосед, услышав шум, вызвал полицию. (в середине)'),
    ('She sat on the sofa {{reading a book}}. (no comma)', 'Она сидела на диване, читая книгу. (без запятой в английском)'),
    ('Tom arrived {{looking exhausted}}. (no comma)', 'Том приехал, выглядя измотанным. (без запятой в английском)')], PURPLE, L_PURPLE))
story.append(point('6.2', 'Where do we use it? Writing and speaking', 'Где мы это используем? Письмо и речь', [
    ('Participle clauses are most common in writing, stories, news and formal speech. In casual talk we often choose because, while or and.',
     'Причастные обороты чаще всего встречаются в письме, рассказах, новостях и формальной речи. В неформальной речи мы часто выбираем because, while или and.')], [
    ('{{Having received your message}}, I am writing to confirm the meeting. (email)', 'Получив ваше сообщение, пишу, чтобы подтвердить встречу. (письмо)'),
    ('{{Using a sharp knife}}, cut the onion into rings. (recipe)', 'Используя острый нож, нарежьте лук кольцами. (рецепт)'),
    ('{{Hearing a strange noise}}, Lily froze. (story)', 'Услышав странный шум, Лили замерла. (рассказ)'),
    ('{{Speaking to reporters}}, the minister denied the rumours. (news)', 'Выступая перед журналистами, министр опроверг слухи. (новости)'),
    ('{{Thinking about it now}}, I should have called. (speaking)', 'Если подумать сейчас, мне следовало позвонить. (разговор)')], PURPLE, L_PURPLE))

# ============ PART 7 ============
part('7', 'Watch out! Common mistakes', 'Осторожно! Частые ошибки', PINK)
story.append(point('7.1', 'Mistake: -ing or V3? (wrong voice)', 'Ошибка: -ing или V3? (неверный залог)', [
    ('If a feeling or state is caused by something, use V3 for the person who feels it.',
     'Если чувство или состояние вызвано чем-то, используйте V3 для того, кто это чувствует.')], [
    ('Surprising by the news, he sat down.', 'Удивляющий новостью, он сел. (неверно)',
     '{{Surprised}} by the news, he sat down.', 'Удивлённый новостью, он сел.'),
    ('Locking out of the flat, I called my landlord.', 'Запирая снаружи квартиру, я позвонил хозяину. (неверно)',
     '{{Locked out}} of the flat, I called my landlord.', 'Оказавшись запертым снаружи, я позвонил хозяину квартиры.'),
    ('Interesting in art, she visited the gallery.', 'Интересный в искусстве, она посетила галерею. (неверно)',
     '{{Interested}} in art, she visited the gallery.', 'Интересуясь искусством, она посетила галерею.'),
    ('Tiring from the trip, he slept all day.', 'Утомляющий поездкой, он проспал весь день. (неверно)',
     '{{Tired}} from the trip, he slept all day.', 'Устав от поездки, он проспал весь день.'),
    ('Shocking by the price, she left the shop.', 'Шокирующая ценой, она вышла из магазина. (неверно)',
     '{{Shocked}} by the price, she left the shop.', 'Шокированная ценой, она вышла из магазина.')], PINK, L_PINK, pairs=True))
story.append(point('7.2', 'Mistake: wrong time order', 'Ошибка: неверная последовательность', [
    ('If the first action is complete before the second begins, use having + V3, not V-ing.',
     'Если первое действие завершилось до начала второго, используйте having + V3, а не V-ing.')], [
    ('Taking a shower, I put on clean clothes.', 'Принимая душ, я надел чистую одежду. (неверно)',
     '{{Having taken}} a shower, I put on clean clothes.', 'Приняв душ, я надел чистую одежду.'),
    ('Writing the email, I pressed send.', 'Пиша письмо, я нажал "отправить". (неверно)',
     '{{Having written}} the email, I pressed send.', 'Написав письмо, я нажал "отправить".'),
    ('Paying the bill, we left the restaurant.', 'Оплачивая счёт, мы вышли из ресторана. (менее точно)',
     '{{Having paid}} the bill, we left the restaurant.', 'Оплатив счёт, мы вышли из ресторана.'),
    ('Finishing my homework, I went out with friends.', 'Заканчивая домашнее задание, я пошёл гулять с друзьями. (менее точно)',
     '{{Having finished}} my homework, I went out with friends.', 'Закончив домашнее задание, я пошёл гулять с друзьями.'),
    ('Eating dinner, I washed up.', 'Ужиная, я помыл посуду. (менее точно)',
     '{{Having eaten}} dinner, I washed up.', 'Поужинав, я помыл посуду.')], PINK, L_PINK, pairs=True))
story.append(point('7.3', 'Mistake: the missing comma', 'Ошибка: пропущенная запятая', [
    ('After an opening participle clause, always put a comma.', 'После вводного причастного оборота всегда ставьте запятую.')], [
    ('Feeling tired I went to bed.', 'Почувствовав усталость я пошёл спать. (нет запятой)',
     '{{Feeling tired}}, I went to bed.', 'Почувствовав усталость, я пошёл спать.'),
    ('Having locked the door we left.', 'Заперев дверь мы ушли. (нет запятой)',
     '{{Having locked the door}}, we left.', 'Заперев дверь, мы ушли.'),
    ('Not knowing the answer she guessed.', 'Не зная ответа она угадывала. (нет запятой)',
     '{{Not knowing the answer}}, she guessed.', 'Не зная ответа, она наугад выбрала вариант.'),
    ('Stuck in traffic Dan phoned the office.', 'Застряв в пробке Дэн позвонил в офис. (нет запятой)',
     '{{Stuck in traffic}}, Dan phoned the office.', 'Застряв в пробке, Дэн позвонил в офис.'),
    ('Opening the box we found a note.', 'Открыв коробку мы нашли записку. (нет запятой)',
     '{{Opening the box}}, we found a note.', 'Открыв коробку, мы нашли записку.')], PINK, L_PINK, pairs=True))

# ============ CHEAT SHEET ============
story.append(PageBreak())
story.append(banner('8', 'Cheat sheet: quick test', 'Шпаргалка: быстрый тест', CORAL)); story.append(sp(9))
def q(n, en, ru, col):
    return Paragraph(f'<para alignment="center"><font name="DV-B" size="9" color="white">{n}</font><br/><font name="DV" size="8.5" color="white">{en}</font><br/><font name="DV-I" size="7.8" color="white">{ru}</font></para>', st_en)
qs = Table([[q('QUESTION 1','Did it happen EARLIER?<br/>YES: having + V3','Это произошло РАНЬШЕ?<br/>ДА: having + V3',''),
             q('QUESTION 2','Is it DONE TO the subject?<br/>YES: V3 / having been V3','Это делают С подлежащим?<br/>ДА: V3 / having been V3',''),
             q('QUESTION 3','Is it NEGATIVE?<br/>Put NOT first','Это ОТРИЦАНИЕ?<br/>Поставьте NOT первым','')]], colWidths=[W/3]*3)
qs.setStyle(TableStyle([('BACKGROUND',(0,0),(0,0),CORAL),('BACKGROUND',(1,0),(1,0),ORANGE),('BACKGROUND',(2,0),(2,0),GREEN),('ROUNDEDCORNERS',[10,10,10,10]),
    ('VALIGN',(0,0),(-1,-1),'MIDDLE'),('TOPPADDING',(0,0),(-1,-1),9),('BOTTOMPADDING',(0,0),(-1,-1),9),('LINEAFTER',(0,0),(1,0),2,colors.white)]))
story.append(qs); story.append(sp(9))
cs = [(CORAL,L_CORAL,'Same subject','{{Opening the door}}, I found a parcel.','Открыв дверь, я нашёл посылку.'),
      (ORANGE,L_ORANGE,'V-ing = same time','{{Reading the menu}}, she smiled.','Читая меню, она улыбнулась.'),
      (GOLD,L_GOLD,'V3 = passive','{{Surprised by the news}}, he sat down.','Удивлённый новостью, он сел.'),
      (GREEN,L_GREEN,'having + V3 = earlier','{{Having locked the door}}, I left.','Заперев дверь, я ушёл.'),
      (BLUE,L_BLUE,'having been + V3','{{Having been warned}}, they took umbrellas.','Предупреждённые, они взяли зонты.'),
      (PINK,L_PINK,'not + participle','{{Not feeling well}}, she stayed at home.','Плохо себя чувствуя, она осталась дома.'),
      (PURPLE,L_PURPLE,'Comma after opening clause','{{Feeling hungry}}, I made a sandwich.','Почувствовав голод, я сделал бутерброд.'),
      (CORAL,L_CORAL,'Joining words can stay','{{While waiting}} for the bus, I read the news.','Пока я ждал автобус, я читал новости.')]
for col, lcol, ttl, en, ru in cs:
    tc = '#1D3557' if col in (GOLD,) else 'white'
    t = Table([[Paragraph(f'<font name="DV-B" size="9.5" color="{tc}">{ttl}</font>', st_en), pair(mk(en,col), ru, small=True)]], colWidths=[W*0.3, W*0.7])
    t.setStyle(TableStyle([('BACKGROUND',(0,0),(0,0),col),('BACKGROUND',(1,0),(1,0),lcol),('VALIGN',(0,0),(0,0),'MIDDLE'),('ROUNDEDCORNERS',[10,10,10,10]),
        ('LEFTPADDING',(0,0),(-1,-1),9),('TOPPADDING',(0,0),(-1,-1),6),('BOTTOMPADDING',(0,0),(-1,-1),1)]))
    story += [t, sp(6)]
story.append(sp(4))
story.append(card(pair('Ready? Turn the page for your worksheet: 9 missions from noticing to creating!', 'Готовы? Переверните страницу: рабочий лист из 9 миссий, от узнавания до создания!', center=True), L_PINK, PINK))

# ================= WORKSHEET =================
def line(n=1, gap=14):
    return [HRFlowable(width='100%', thickness=0.6, color=HexColor('#9AA5B1'), spaceBefore=gap, spaceAfter=0) for _ in range(n)]
def item(num, en, ru, col):
    return [Paragraph(f'<font name="DV-B" color="{DARK[id(col)]}">{num}.</font> {en}', st_sm), Paragraph(ru, st_smru)]
def mission(num, title, ru, col, lcol, instr, instr_ru, body):
    story.append(banner(num, title, ru, col)); story.append(sp(6))
    story.append(card(pair(instr, instr_ru, rule=False, small=True), colors.white, col, pad=5)); story.append(sp(6))
    story.extend(body)
    story.append(sp(10))

story.append(PageBreak())
# title
tt = Table([[[Paragraph('<font name="DV-B" size="20" color="white">PARTICIPLE CLAUSES LAB</font>', st_en),
              Paragraph('<font name="DV-I" size="10" color="white">Worksheet: 9 missions to master participle clauses  |  Рабочий лист: 9 миссий для уверенного владения причастными оборотами</font>', st_en)]]], colWidths=[W])
tt.setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),PURPLE),('ROUNDEDCORNERS',[14,14,14,14]),('TOPPADDING',(0,0),(-1,-1),12),('BOTTOMPADDING',(0,0),(-1,-1),12),('LEFTPADDING',(0,0),(-1,-1),14)]))
story += [tt, sp(6)]
nm = Table([[Paragraph('<font name="DV-B" size="9">Name / Имя: ____________________</font>', st_en), Paragraph('<font name="DV-B" size="9">Date / Дата: ______________</font>', st_en), Paragraph('<font name="DV-B" size="9">Score / Баллы: ______ / 64</font>', st_en)]], colWidths=[W*0.4, W*0.3, W*0.3])
story += [nm, sp(8)]
story.append(card(pair('How it works: the missions go from NOTICING (1-2) to CHOOSING (3-4) to PRODUCING (5-7) to CREATING (8-9). Do them in order, check your answers in the key, then re-read any rule you missed.',
                       'Как это работает: миссии идут от УЗНАВАНИЯ (1-2) к ВЫБОРУ (3-4), затем к СОСТАВЛЕНИЮ (5-7) и к ТВОРЧЕСТВУ (8-9). Выполняйте по порядку, проверяйте ответы по ключу и перечитывайте правила, в которых ошиблись.', small=True), L_GOLD, GOLD, pad=6))
story.append(sp(10))

# --- M1
txt = [('Waking up late, Sam grabbed his phone.', 'Проснувшись поздно, Сэм схватил телефон.'),
       ('Not having charged it the night before, he found it dead.', 'Не зарядив его накануне вечером, он обнаружил, что телефон разрядился.'),
       ('Annoyed by the silent alarm, he rushed into the shower.', 'Раздражённый молчащим будильником, он бросился в душ.'),
       ('Shivering under the cold water, he got dressed in two minutes.', 'Дрожа под холодной водой, он оделся за две минуты.'),
       ('Running to the bus stop, he saw the bus drive away.', 'Подбегая к остановке, он увидел, как автобус уезжает.'),
       ('Having no money for a taxi, he walked to work, arriving an hour late.', 'Не имея денег на такси, он пошёл на работу пешком, опоздав на час.'),
       ('Seeing his face, his boss decided not to shout.', 'Увидев его лицо, начальник решил не кричать.')]
body = []
f = []
for i,(e,r) in enumerate(txt,1): f += item(i, e, r, CORAL)
body.append(card(f, L_CORAL, CORAL, pad=6)); body.append(sp(6))
body.append(Paragraph('Form: <b>A</b> V-ing   <b>B</b> V3   <b>C</b> having + V3 / having been + V3   <b>D</b> not / never + participle', st_sm))
body.append(Paragraph('Форма: A - V-ing; B - V3; C - having + V3 / having been + V3; D - not / never + причастие', st_smru))
rows = [[Paragraph('<b>#</b>', st_sm), Paragraph('<b>Underline the clause, then write its first words / Первые слова оборота</b>', st_sm), Paragraph('<b>Form A-D</b>', st_sm), Paragraph('<b>Meaning / Значение</b><br/><font size="7.5">reason, time, result</font>', st_sm)]]
for i in range(1,8): rows.append([Paragraph(str(i), st_sm), '', '', ''])
t = Table(rows, colWidths=[22, W*0.46, W*0.15, W*0.28-22+22], rowHeights=[28]+[22]*7)
t.setStyle(TableStyle([('GRID',(0,0),(-1,-1),0.6,CORAL),('BACKGROUND',(0,0),(-1,0),L_CORAL),('VALIGN',(0,0),(-1,-1),'MIDDLE')]))
body.append(t)
mission('M1', 'Mission 1: Detective Mode', 'Миссия 1: Режим детектива', CORAL, L_CORAL,
        'Read Sam\'s terrible Monday. Underline every participle clause. Then decide its form (A-D) and its meaning. (14 points: 7 forms + 7 meanings)',
        'Прочитайте ужасный понедельник Сэма. Подчеркните каждый причастный оборот. Затем определите его форму (A-D) и значение. (14 баллов: 7 форм + 7 значений)', body)

# --- M2 meaning match
pm = [('Crossing the street, Ben checked his messages.', 'Переходя улицу, Бен проверил сообщения.'),
      ('Not having eaten since breakfast, Ana was starving.', 'Не евши с завтрака, Ана умирала от голода.'),
      ('Cooked for too long, pasta becomes soft.', 'Если варить пасту слишком долго, она становится мягкой.'),
      ('Having trained for months, Leo still lost the race.', 'Тренировавшись месяцами, Лео всё равно проиграл забег.'),
      ('The bus broke down, making everyone late.', 'Автобус сломался, из-за чего все опоздали.'),
      ('Having cleared the table, Ola started the dishes.', 'Убрав со стола, Ола принялась за посуду.')]
po = [('A', 'If pasta is cooked for too long, ...', 'Если пасту варить слишком долго, ...'),
      ('B', 'Because Ana had not eaten since breakfast, ...', 'Так как Ана не ела с завтрака, ...'),
      ('C', 'While Ben was crossing the street, ...', 'Пока Бен переходил улицу, ...'),
      ('D', 'Although Leo had trained for months, ...', 'Хотя Лео тренировался месяцами, ...'),
      ('E', '..., which made everyone late.', '..., что заставило всех опоздать.'),
      ('F', 'After Ola had cleared the table, ...', 'После того как Ола убрала со стола, ...')]
f1 = []; 
for i,(e,r) in enumerate(pm,1): f1 += [Paragraph(f'<font name="DV-B" color="#C26A00">{i}.</font> {e} &#8594; <b>____</b>', st_sm), Paragraph(r, st_smru)]
f2 = []
for k,e,r in po: f2 += [Paragraph(f'<font name="DV-B" color="#C26A00">{k}.</font> {e}', st_sm), Paragraph(r, st_smru)]
t = Table([[f1, f2]], colWidths=[W*0.52, W*0.48])
t.setStyle(TableStyle([('VALIGN',(0,0),(-1,-1),'TOP'),('BACKGROUND',(0,0),(0,0),L_ORANGE),('BACKGROUND',(1,0),(1,0),colors.white),('BOX',(0,0),(-1,-1),1,ORANGE),('LINEAFTER',(0,0),(0,0),1,ORANGE),
    ('ROUNDEDCORNERS',[8,8,8,8]),('LEFTPADDING',(0,0),(-1,-1),8),('TOPPADDING',(0,0),(-1,-1),6)]))
mission('M2', 'Mission 2: Meaning Match', 'Миссия 2: Подбери значение', ORANGE, L_ORANGE,
        'Match each participle sentence (1-6) with the paraphrase (A-F) that gives its hidden meaning. Write the letter on the line. (6 points)',
        'Соедините каждое предложение с причастным оборотом (1-6) с перефразированием (A-F), которое раскрывает скрытое значение. Впишите букву. (6 баллов)', [t])

# --- M3 timeline
story.append(PageBreak())
tl = [('I walked along the beach. I listened to music.', '(same time)', 'Я шёл по пляжу. Я слушал музыку.', '(одновременно)'),
      ('She finished her coffee. She left the café.', '(1st action, then 2nd)', 'Она допила кофе. Она вышла из кафе.', '(сначала первое, потом второе)'),
      ('He waited for his flight. He read a novel.', '(same time)', 'Он ждал свой рейс. Он читал роман.', '(одновременно)'),
      ('We checked the weather forecast. We packed our umbrellas.', '(1st action, then 2nd)', 'Мы проверили прогноз погоды. Мы взяли зонты.', '(сначала первое, потом второе)'),
      ('The kids were playing in the garden. They got very dirty.', '(same time)', 'Дети играли в саду. Они сильно испачкались.', '(одновременно)'),
      ('I read the instructions. I assembled the shelf.', '(1st action, then 2nd)', 'Я прочитал инструкцию. Я собрал полку.', '(сначала первое, потом второе)')]
f = []
for i,(e,tg,r,trr) in enumerate(tl,1):
    f += [Paragraph(f'<font name="DV-B" color="#0B8A7D">{i}.</font> {e} <font name="DV-B" color="#0B8A7D">{tg}</font>', st_sm), Paragraph(f'{r} {trr}', st_smru)]
    f += line(1, 15)
    f.append(sp(3))
mission('M3', 'Mission 3: Timeline Lab', 'Миссия 3: Лаборатория времени', GREEN, L_GREEN,
        'Join each pair into ONE sentence that starts with a participle clause. Same time = V-ing. First, then = having + V3. (6 points)',
        'Объедините каждую пару в ОДНО предложение, которое начинается с причастного оборота. Одновременно = V-ing. Сначала, потом = having + V3. (6 баллов)', [card(f, L_GREEN, GREEN, pad=6)])

# --- M4 multiple choice
mc = [('___ the news on the radio, Dad turned the volume up.', 'a) Hearing   b) Heard   c) To hear', 'Услышав новости по радио, папа сделал звук громче.'),
      ('___ by the noise, the baby started crying.', 'a) Waking   b) Woken   c) To wake', 'Разбуженный шумом, малыш заплакал.'),
      ('___ the keys, she couldn\'t get in.', 'a) Lose   b) Having lost   c) Lost', 'Потеряв ключи, она не смогла войти.'),
      ('___ a car, Tom goes everywhere by bike.', 'a) Not having   b) Not to have   c) No having', 'Не имея машины, Том ездит везде на велосипеде.'),
      ('___ to my sister, I forgot the time.', 'a) Talk   b) Talking   c) Talked', 'Разговаривая с сестрой, я забыл о времени.'),
      ('___ in a hurry, the report was full of mistakes.', 'a) Writing   b) Written   c) Having wrote', 'Написанный в спешке, отчёт был полон ошибок.'),
      ('___ three hours in the queue, we finally reached the counter.', 'a) Spending   b) Having spent   c) Spent', 'Проведя три часа в очереди, мы наконец дошли до стойки.'),
      ('___ by the price, she put the shoes back.', 'a) Shocking   b) Shocked   c) Shock', 'Шокированная ценой, она положила туфли обратно.'),
      ('___ the shop was closed, we went home.', 'a) Telling   b) Having been told   c) Having told', 'Узнав, что магазин закрыт, мы пошли домой.'),
      ('___ a student, Mia gets cheaper train tickets.', 'a) Be   b) Being   c) Been', 'Будучи студенткой, Миа получает более дешёвые билеты на поезд.')]
f = []
for i,(e,o,r) in enumerate(mc,1):
    f += [Paragraph(f'<font name="DV-B" color="#1F5FD6">{i}.</font> {e} &nbsp;&nbsp;<font name="DV-B">{o}</font>', st_sm), Paragraph(r, st_smru)]
mission('M4', 'Mission 4: Form Factory', 'Миссия 4: Фабрика форм', BLUE, L_BLUE,
        'Circle the correct form a, b or c. Think: same time, earlier, passive, or negative? (10 points)',
        'Обведите правильную форму a, b или c. Подумайте: одновременно, раньше, страдательное значение или отрицание? (10 баллов)', [card(f, L_BLUE, BLUE, pad=6)])

# --- M5 story cloze
story.append(PageBreak())
sc = [('___ (feel) lazy, Ellie stayed in bed until noon.', 'Чувствуя лень, Элли пролежала в постели до полудня.'),
      ('___ (not / have) any food at home, she ordered a burger online.', 'Не имея дома еды, она заказала бургер онлайн.'),
      ('___ (eat) too fast, she spilled ketchup on her new white top.', 'Слишком быстро ела, она пролила кетчуп на свою новую белую кофту.'),
      ('___ (cover) in red stains, the top looked terrible.', 'Покрытая красными пятнами, кофта выглядела ужасно.'),
      ('___ (know) that her friends were arriving at six, she rushed to the sink.', 'Зная, что друзья придут в шесть, она бросилась к раковине.'),
      ('___ (leave) the top in cold water, she went to tidy her hair.', 'Оставив кофту в холодной воде, она пошла поправить волосы.'),
      ('The doorbell rang, ___ (make) her jump.', 'Прозвенел дверной звонок, заставив её вздрогнуть.'),
      ('___ (see) her wet sleeves, her friends burst out laughing.', 'Увидев её мокрые рукава, друзья расхохотались.')]
f = []
for i,(e,r) in enumerate(sc,1): f += item(i, e, r, PURPLE)
mission('M5', 'Mission 5: Story Studio', 'Миссия 5: Студия историй', PURPLE, L_PURPLE,
        'Read "Ellie\'s Saturday Disaster". Write the correct participle form of the verb in brackets. Choose: -ing, V3, having + V3, or not + participle. (8 points)',
        'Прочитайте "Субботняя катастрофа Элли". Впишите правильную форму причастия от глагола в скобках: -ing, V3, having + V3 или not + причастие. (8 баллов)', [card(f, L_PURPLE, PURPLE, pad=6)])

# --- M6 sentence surgeon
ss = [('I was feeling tired. I went to bed early.', '(-ing)', 'Я чувствовал усталость. Я рано лёг спать.'),
      ('She was shocked by the news. She dropped her cup.', '(V3)', 'Она была шокирована новостью. Она уронила чашку.'),
      ('We had booked the tickets online. We skipped the queue.', '(having + V3)', 'Мы забронировали билеты онлайн. Мы пропустили очередь.'),
      ('He didn\'t know the password. He called the IT desk.', '(not + -ing)', 'Он не знал пароль. Он позвонил в службу поддержки.'),
      ('The kids had been left alone for an hour. They built a fort from cushions.', '(having been + V3)', 'Детей оставили одних на час. Они построили крепость из подушек.'),
      ('The storm destroyed the fence. It also flooded the garden.', '(result, V-ing at the end)', 'Буря разрушила забор. Она также затопила сад.'),
      ('I opened the oven. I burnt my hand.', '(-ing, same time)', 'Я открыл духовку. Я обжёг руку.'),
      ('Zoe was the oldest sibling. She looked after the others.', '(being)', 'Зои была старшей из детей. Она присматривала за остальными.')]
f = []
for i,(e,h,r) in enumerate(ss,1):
    f += [Paragraph(f'<font name="DV-B" color="#D6195E">{i}.</font> {e} <font name="DV-B" color="#D6195E">{h}</font>', st_sm), Paragraph(r, st_smru)] + line(1, 14) + [sp(2)]
mission('M6', 'Mission 6: Sentence Surgeon', 'Миссия 6: Хирург предложений', PINK, L_PINK,
        'Operate! Combine the two sentences into ONE sentence with a participle clause, using the form in brackets. (8 points)',
        'Операция! Объедините два предложения в ОДНО с причастным оборотом, используя форму в скобках. (8 баллов)', [card(f, L_PINK, PINK, pad=6)])

# --- M7 dangling doctor
story.append(PageBreak())
dd = [('Sitting on the sofa, the dog jumped onto my lap.', 'Сидя на диване, собака прыгнула мне на колени.'),
      ('Cooking dinner, the smoke alarm went off.', 'Готовя ужин, сработала пожарная сигнализация.'),
      ('Tired after the trip, the bed looked wonderful.', 'Уставшая после поездки, кровать выглядела чудесно.'),
      ('Not knowing the way, the map was useless.', 'Не зная дороги, карта была бесполезной.'),
      ('Waiting at the bus stop, a car splashed me.', 'Ожидая на остановке, машина обрызгала меня.'),
      ('Having watched the film, the lights came on.', 'Посмотрев фильм, включился свет.')]
f = []
for i,(e,r) in enumerate(dd,1):
    f += [Paragraph(f'<font name="DV-B" color="#6D28D9">{i}.</font> {e}', st_sm), Paragraph(r, st_smru),
          Paragraph('Who is doing the action in the participle clause? ______________ &nbsp;&nbsp;Fix: ', st_smru)] + line(1, 14) + [sp(2)]
mission('M7', 'Mission 7: Dangling Doctor', 'Миссия 7: Доктор висячих причастий', PURPLE, L_PURPLE,
        'Each sentence has a "dangling" participle. First write WHO is really doing the action. Then rewrite the sentence correctly. (6 points)',
        'В каждом предложении есть "висячее" причастие. Сначала напишите, КТО на самом деле выполняет действие. Затем перепишите предложение правильно. (6 баллов)', [card(f, L_PURPLE, PURPLE, pad=6)])

# --- M8 create zone
dice1 = [('1', 'V-ing', 'Walking / Waiting / Cooking ...'), ('2', 'having + V3', 'Having finished / Having lost ...'), ('3', 'not + participle', 'Not wanting / Not knowing ...'),
         ('4', 'V3', 'Tired / Surprised / Left alone ...'), ('5', 'Being ...', 'Being late / Being a student ...'), ('6', 'Joker', 'while / after / although + participle')]
dice2 = ['at the airport / в аэропорту', 'in a café / в кафе', 'at work / на работе', 'on the phone / по телефону', 'at the supermarket / в супермаркете', 'at a party / на вечеринке']
r1 = [[Paragraph('<b>Die 1: form / Кубик 1: форма</b>', st_sm)]] + [[Paragraph(f'<b>{a}</b>  {b}: <i>{c}</i>', st_sm)] for a,b,c in dice1]
r2 = [[Paragraph('<b>Die 2: place / Кубик 2: место</b>', st_sm)]] + [[Paragraph(f'<b>{i}</b>  {x}', st_sm)] for i, x in enumerate(dice2, 1)]
dt = Table([[Table(r1, colWidths=[W*0.56]), Table(r2, colWidths=[W*0.36])]], colWidths=[W*0.58, W*0.38])
dt.setStyle(TableStyle([('VALIGN',(0,0),(-1,-1),'TOP')]))
f = [dt, sp(4)]
f += pair('<b>Game A: Dice story.</b> Roll two dice (or choose numbers). Write a sentence that uses the form from Die 1 and happens in the place from Die 2. Write 4 sentences.',
          '<b>Игра A: Кубики.</b> Бросьте два кубика (или выберите числа). Напишите предложение с формой с кубика 1, действие которого происходит в месте с кубика 2. Напишите 4 предложения.', small=True)
f += pair('Model: 3 + 2 &#8594; Not having any cash, I paid by card at the café.', 'Образец: 3 + 2 &#8594; Не имея наличных, я заплатил картой в кафе.', small=True)
for i in range(1,5): f += [Paragraph(f'<font name="DV-B" color="#C26A00">{i}.</font>', st_sm)] + line(1, 12)
f += [sp(6)]
f += pair('<b>Game B: Excuse generator.</b> You arrive 30 minutes late for class. Write 3 excuses: one with V-ing, one with having + V3, one with V3.',
          '<b>Игра B: Генератор оправданий.</b> Вы опоздали на занятие на 30 минут. Напишите 3 оправдания: одно с V-ing, одно с having + V3, одно с V3.', small=True)
f += pair('Model: Stuck in a lift for twenty minutes, I couldn\'t send you a message.', 'Образец: Застряв в лифте на двадцать минут, я не смог отправить вам сообщение.', small=True)
for i in range(1,4): f += [Paragraph(f'<font name="DV-B" color="#C26A00">{i}.</font>', st_sm)] + line(1, 12)
f += [sp(6)]
f += pair('<b>Game C: Partner check.</b> Read your best sentence aloud. Your partner says the form and the meaning (reason, time, result, condition, contrast).',
          '<b>Игра C: Проверка с партнёром.</b> Прочитайте вслух своё лучшее предложение. Партнёр называет форму и значение (причина, время, результат, условие, уступка).', small=True)
mission('M8', 'Mission 8: Create Zone', 'Миссия 8: Творческая зона', ORANGE, L_ORANGE,
        'Now you are the author! Use your own ideas. Your teacher gives 1 point for each correct sentence (7 points possible, 6 count towards your score).',
        'Теперь вы автор! Используйте свои идеи. Преподаватель ставит 1 балл за каждое правильное предложение (возможно 7 баллов, в итоговый счёт идут 6).', [card(f, L_ORANGE, ORANGE, pad=7)])

# --- M9 boss translate
story.append(PageBreak())
tr = [('Вернувшись домой, я сразу включил чайник.', 'Hint: having + V3 or -ing', 'Returning/Having returned home...'),
      ('Не зная, что сказать, она улыбнулась.', 'Hint: not + -ing', ''),
      ('Разбуженный будильником, он нехотя встал.', 'Hint: V3', ''),
      ('Поужинав, мы пошли гулять.', '', ''),
      ('Опаздывая на встречу, Лена взяла такси.', '', ''),
      ('Магазин закрылся рано, и мы остались без хлеба.', '', '')]
f = []
for i,(r,h,_) in enumerate(tr,1):
    hint = f' <font name="DV-I" size="8" color="#6D28D9">({h})</font>' if h else ''
    f += [Paragraph(f'<font name="DV-B" color="#6D28D9">{i}.</font> {r}{hint}', st_en)] + line(1, 15) + [sp(3)]
mission('M9', 'Mission 9: Boss Level, Translate!', 'Миссия 9: Финальный босс, переведите!', PURPLE, L_PURPLE,
        'Translate the Russian sentences into English. Each answer must contain a participle clause. Hints help you in 1-3 only. (6 points)',
        'Переведите русские предложения на английский. В каждом ответе должен быть причастный оборот. Подсказки есть только в 1-3. (6 баллов)', [card(f, L_PURPLE, PURPLE, pad=7)])

# --- scoreboard
sb = [['Mission', 'M1', 'M2', 'M3', 'M4', 'M5', 'M6', 'M7', 'M8', 'M9', 'Total'],
      ['Max', '14', '6', '6', '10', '8', '8', '6', '6', '6', '64'],
      ['My score / Мои баллы', '', '', '', '', '', '', '', '', '', '']]
t = Table([[Paragraph(f'<b>{c}</b>' if r < 2 else c, st_sm) for c in row] for r, row in enumerate(sb)], colWidths=[W*0.24]+[W*0.076]*9+[W*0.076], rowHeights=[20,20,26])
t.setStyle(TableStyle([('GRID',(0,0),(-1,-1),0.7,PINK),('BACKGROUND',(0,0),(-1,0),L_PINK),('VALIGN',(0,0),(-1,-1),'MIDDLE')]))
f = [t, sp(6)]
for lv, lvr in [('58-64: Participle Pro! You can use them naturally.', '58-64: Профи по причастным оборотам! Вы используете их естественно.'),
                ('45-57: Almost there. Re-read the rules you missed.', '45-57: Почти готово. Перечитайте правила, в которых ошиблись.'),
                ('30-44: Good start. Redo Missions 3 and 4 after revising Parts 2 and 3.', '30-44: Хорошее начало. Повторите миссии 3 и 4 после повторения частей 2 и 3.'),
                ('0-29: Go back to Parts 1-2 and try again tomorrow.', '0-29: Вернитесь к частям 1-2 и попробуйте снова завтра.')]:
    f += pair(lv, lvr, small=True)
f += [Paragraph('<b>I can ... / Я умею ...</b>', st_sm)]
for c1, c2 in [('&#9744; recognise V-ing, V3 and having + V3 clauses', 'узнавать обороты с V-ing, V3 и having + V3'),
               ('&#9744; check that the subject matches the main clause', 'проверять, что подлежащее совпадает с главным предложением'),
               ('&#9744; choose the right time order', 'выбирать верную последовательность во времени'),
               ('&#9744; use participle clauses in my own writing and speaking', 'использовать причастные обороты в своей письменной и устной речи')]:
    f += pair(c1, c2, small=True)
story.append(banner('SB', 'Scoreboard and self-check', 'Табло результатов и самопроверка', PINK)); story.append(sp(6))
story.append(card(f, L_PINK, PINK, pad=7))

# ================= ANSWER KEY =================
story.append(PageBreak())
story.append(banner('K', 'Answer key', 'Ключи к заданиям', BLUE)); story.append(sp(8))
def keyblock(title, lines, col, lcol):
    fl = [Paragraph(f'<font name="DV-B" color="{DARK[id(col)]}">{title}</font>', st_sm)] + [Paragraph(l, st_sm) for l in lines]
    return [card(fl, lcol, col, pad=6), sp(6)]
story += keyblock('M1 Detective Mode (form / meaning)', [
    '1. Waking up late: A, time.  2. Not having charged it: D, reason.  3. Annoyed by the silent alarm: B, reason.',
    '4. Shivering under the cold water: A, time.  5. Running to the bus stop: A, time.',
    '6. Having no money for a taxi: A (the -ing form of "have", not perfect), reason. Bonus: arriving an hour late = A, result.',
    '7. Seeing his face: A, time or reason (both accepted).'], CORAL, L_CORAL)
story += keyblock('M2 Meaning Match', ['1-C (time)   2-B (reason)   3-A (condition)   4-D (contrast)   5-E (result)   6-F (time, earlier action)'], ORANGE, L_ORANGE)
story += keyblock('M3 Timeline Lab', [
    '1. Walking along the beach, I listened to music.   2. Having finished her coffee, she left the café.',
    '3. Waiting for his flight, he read a novel.   4. Having checked the weather forecast, we packed our umbrellas.',
    '5. Playing in the garden, the kids got very dirty.   6. Having read the instructions, I assembled the shelf.'], GREEN, L_GREEN)
story += keyblock('M4 Form Factory', ['1-a   2-b   3-b   4-a   5-b   6-b   7-b   8-b   9-b   10-b'], BLUE, L_BLUE)
story += keyblock('M5 Story Studio', ['1. Feeling   2. Not having   3. Eating   4. Covered   5. Knowing   6. Having left   7. making   8. Seeing'], PURPLE, L_PURPLE)
story += keyblock('M6 Sentence Surgeon', [
    '1. Feeling tired, I went to bed early.   2. Shocked by the news, she dropped her cup.',
    '3. Having booked the tickets online, we skipped the queue.   4. Not knowing the password, he called the IT desk.',
    '5. Having been left alone for an hour, the kids built a fort from cushions.',
    '6. The storm destroyed the fence, flooding the garden.   7. Opening the oven, I burnt my hand.',
    '8. Being the oldest sibling, Zoe looked after the others.'], PINK, L_PINK)
story += keyblock('M7 Dangling Doctor (possible fixes)', [
    '1. The dog is jumping. Sitting on the sofa, I felt the dog jump onto my lap.',
    '2. Nobody is named; the alarm cannot cook. Cooking dinner, I set off the smoke alarm.',
    '3. The bed is not tired. Tired after the trip, I thought the bed looked wonderful.',
    '4. The map does not lack knowledge. Not knowing the way, we found the map useless.',
    '5. The car is not waiting. Waiting at the bus stop, I was splashed by a car.',
    '6. The lights did not watch. Having watched the film, we saw the lights come on. (or: When the film ended, the lights came on.)'], PURPLE, L_PURPLE)
story += keyblock('M8 Create Zone', ['Free answers. Check: (1) the participle is the first word(s), (2) the subject matches, (3) a comma follows an opening clause, (4) the form fits the time.'], ORANGE, L_ORANGE)
story += keyblock('M9 Boss Level', [
    '1. Having returned (or: Returning) home, I switched on the kettle at once.   2. Not knowing what to say, she smiled.',
    '3. Woken by the alarm clock, he got up reluctantly.   4. Having had dinner, we went for a walk.',
    '5. Being late (or: Running late) for the meeting, Lena took a taxi.',
    '6. The shop closed early, leaving us without bread.'], PURPLE, L_PURPLE)

doc.build(story)
print('built', OUT)
Running Command

cd /tmp/workspace/participle-handbook/src && python build2.py 2>&1 | tail -15 && python -c " import pypdfium2 as p d=p.PdfDocument('/tmp/workspace/participle-handbook/output/Participle_Clauses_Handbook_v2.pdf') print(len(d)) for i in range(len(d)): d[i].render(scale=0.62).to_pil().save(f'/tmp/workspace/participle-handbook/src/v{i+1:02d}.png') "

Running Command

cd /tmp/workspace/participle-handbook/src && python -c " from PIL import Image import glob fs=sorted(glob.glob('v*.png')) for k in range(0,len(fs),8): ims=[Image.open(f) for f in fs[k:k+8]] w,h=ims[0].size m=Image.new('RGB',(w*4,h*2),'white') for i,im in enumerate(ims): m.paste(im,((i%4)*w,(i//4)*h)) m.save(f'm{k//8}.png'); print(m.size) "

Reading File
Reading File
Reading File
Reading File
Running Command

cd /tmp/workspace/participle-handbook/src && python - <<'EOF' s=open('build2.py',encoding='utf-8').read() s=s.replace("story.append(PageBreak())\n","") s=s.replace("# ============ CHEAT SHEET ============\n","# ============ CHEAT SHEET ============\nstory.append(PageBreak())\n") s=s.replace("# title\n","story.append(PageBreak())\n# title\n") s=s.replace("# ================= ANSWER KEY =================\n","# ================= ANSWER KEY =================\nstory.append(PageBreak())\n") # part -> marker s=s.replace(""" story.append(banner(num, title, ru, col)); story.append(sp(9)) if intro: story.append(card(pair(*intro, rule=False), HexColor('#FFFFFF'), col, pad=6)); story.append(sp(9))""", """ items = [banner(num, title, ru, col), sp(9)] if intro: items += [card(pair(*intro, rule=False), HexColor('#FFFFFF'), col, pad=6), sp(9)] story.append(('PART', items))""") # mission keep together s=s.replace(""" story.append(banner(num, title, ru, col)); story.append(sp(6)) story.append(card(pair(instr, instr_ru, rule=False, small=True), colors.white, col, pad=5)); story.append(sp(6)) story.extend(body) story.append(sp(10))""", """ story.append(KeepTogether([banner(num, title, ru, col), sp(6), card(pair(instr, instr_ru, rule=False, small=True), colors.white, col, pad=5), sp(6)] + body + [sp(10)]))""") s=s.replace("doc.build(story)","""new=[]; i=0 while i < len(story): x = story[i] if isinstance(x, tuple) and x[0]=='PART': new.append(KeepTogether(x[1] + [story[i+1]])); i += 2 else: new.append(x); i += 1 doc.build(new)""") # title fix s=s.replace("""Paragraph('<font name="DV-B" size="20" color="white">PARTICIPLE CLAUSES LAB</font>', st_en), Paragraph('<font name="DV-I" size="10" color="white">""","""Paragraph('<font name="DV-B" size="20" color="white">PARTICIPLE CLAUSES LAB</font>', S('big', fontSize=20, leading=26)), Paragraph('<font name="DV-I" size="9" color="white">""") s=s.replace("""Рабочий лист: 9 миссий для уверенного владения причастными оборотами</font>', st_en)]]""","""Рабочий лист: 9 миссий для уверенного владения причастными оборотами</font>', S('sub', fontSize=9, leading=13))]]""") # meaning match blank first s=s.replace("""{i}.</font> {e} &#8594; <b>____</b>'""","""{i}.</font> <b>[ ____ ]</b> {e}'""") open('build2.py','w',encoding='utf-8').write(s) EOF python build2.py 2>&1 | tail -5 && python -c " import pypdfium2 as p, glob, os for f in glob.glob('v*.png')+glob.glob('m*.png'): os.remove(f) from PIL import Image d=p.PdfDocument('/tmp/workspace/participle-handbook/output/Participle_Clauses_Handbook_v2.pdf') n=len(d); print(n) ims=[d[i].render(scale=0.62).to_pil() for i in range(n)] w,h=ims[0].size for k in range(0,n,8): m=Image.new('RGB',(w*4,h*2),'white') for j,im in enumerate(ims[k:k+8]): m.paste(im,((j%4)*w,(j//4)*h)) m.save(f'm{k//8}.png') for i in (15,): d[i].render(scale=1.0).to_pil().save('big16.png') "

Reading File
Reading File
Reading File
Running Command

cd /tmp/workspace/participle-handbook/src && sed -i 's/colWidths=\[W\*0.24\]+\[W\*0.076\]\*9+\[W\*0.076\]/colWidths=[W*0.2]+[W*0.08]*10/' build2.py && python build2.py | tail -1 && python -c " import pypdfium2 as p d=p.PdfDocument('/tmp/workspace/participle-handbook/output/Participle_Clauses_Handbook_v2.pdf'); print(len(d)) d[23].render(scale=0.9).to_pil().crop((0,380,540,760)).save('sb.png')"

Reading File
Running Command

cd /tmp/workspace/participle-handbook/src && python -c " import pypdfium2 as p d=p.PdfDocument('/tmp/workspace/participle-handbook/output/Participle_Clauses_Handbook_v2.pdf') im=d[23].render(scale=0.9).to_pil(); print(im.size) im.crop((0,int(im.size[1]*0.5),im.size[0],int(im.size[1]*0.95))).save('/tmp/workspace/participle-handbook/src/sb.png')"; ls /tmp/workspace/participle-handbook/src | head

Reading File
The new handbook is a 25-page colourful PDF. I rendered all the pages and checked the layout.

Participle Clauses Handbook v2

PDF Document · PDF

Your three changes
  1. 5 daily-life examples for every rule. There are 26 rule points, and each has 5 English examples with the Russian translation under each one. The rules also have a Russian translation. The points are:
    • the big idea
    • tense
    • V-ing
    • V3
    • having + V3
    • having been + V3
    • the negative
    • being
    • time order
    • the same-subject rule
    • fixing dangling participles
    • fixed phrases
    • the bonus clause with its own subject
    • joining words (two points)
    • five meanings (reason, time, result, condition, contrast)
    • position and commas
    • style
    • three common mistakes
    • a Russian helper
    In the mistake sections, each example is a ✘ sentence with a ✔ fix, both translated.
  2. No relative clauses. I removed every trace of them: the section, the exercises, the "who/which" examples and the noun-modifying examples. The handbook now covers only adverbial participle clauses.
  3. Worksheet with 9 missions. It starts on page 17 and goes from noticing to choosing, then producing, then creating.
MissionTaskPoints
M1 Detective ModeFind the participle clauses in a short story and label their form and meaning14
M2 Meaning MatchMatch each participle sentence to its hidden meaning6
M3 Timeline LabChoose between -ing and having + V36
M4 Form FactoryMultiple choice10
M5 Story StudioFill in the gaps in a story8
M6 Sentence SurgeonCombine two sentences into one with a participle clause8
M7 Dangling DoctorName who is really doing the action, then fix the sentence6
M8 Create ZoneA dice game, an excuse generator and a partner check6
M9 Boss LevelTranslate Russian sentences into English6
The worksheet ends with a 64-point scoreboard, level bands and an "I can..." checklist. The last page is an answer key. Every worksheet sentence has a Russian translation under it. The key gives English answers only.
Things to know
  • The Russian translations are mine, so please skim them for natural phrasing. A few are deliberately literal to show the grammar. Examples are "Едя на работу, олень…" for the dangling-participle mistakes and the "(неверно)" lines.
  • Part 3.4 (a participle clause with its own subject, as in "Weather permitting…") goes beyond your coursebook page. I labelled it a C1 bonus.
  • Mission 8 is open-ended, so the teacher marks it.
  • Some pages have white space at the bottom, because I kept each rule box on one page rather than splitting it across two.

Retry please

Running Command

cd /tmp/workspace/participle-handbook/src && python - <<'EOF' import re s=open('build2.py',encoding='utf-8').read() a=s.index("def point("); b=s.index("# ---------- page decorations") new='''def point(code, title, ru_title, rules, exs, col, lcol, pairs=False): dk = DARK.get(id(col)) hd = Table([[Paragraph(f'<font name="DV-B" size="10.5" color="white">{code} {title}</font><br/><font name="DV-I" size="8.5" color="white">{ru_title}</font>', st_en)]], colWidths=[W-24]) hd.setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),col),('ROUNDEDCORNERS',[7,7,7,7]),('LEFTPADDING',(0,0),(-1,-1),9),('TOPPADDING',(0,0),(-1,-1),5),('BOTTOMPADDING',(0,0),(-1,-1),5)])) top = [hd, Spacer(1,6)] for e, r in rules: top += pair(mk(e, col), r, rule=True) lab = '5 daily life examples / 5 примеров из повседневной жизни' if not pairs else '5 mistakes and fixes / 5 ошибок и исправлений' top += [Paragraph(f'<font name="DV-B" size="8.5" color="{dk}">{lab}</font>', st_en), Spacer(1,3)] def exrow(i, ex): if not pairs: e, r = ex return [Paragraph(f'<font name="DV-B" color="{dk}">{i}.</font> ' + mk(e, col), st_sm), Paragraph(r, st_smru)] we, wr, re_, rr = ex return [Paragraph(f'<font name="DV-B" color="#D62839">{i}. &#10008;</font> ' + we, st_sm), Paragraph(wr, st_smru), Paragraph('<font name="DV-B" color="#0B8A7D">&#10004;</font> ' + mk(re_, GREEN), st_sm), Paragraph(rr, st_smru)] rows = [[top + exrow(1, exs[0]) + exrow(2, exs[1])]] + [[exrow(i, ex)] for i, ex in enumerate(exs[2:], 3)] t = Table(rows, colWidths=[W]) n = len(rows) t.setStyle(TableStyle([('BACKGROUND',(0,0),(-1,-1),lcol),('BOX',(0,0),(-1,-1),1.3,col),('ROUNDEDCORNERS',[10,10,10,10]), ('LEFTPADDING',(0,0),(-1,-1),12),('RIGHTPADDING',(0,0),(-1,-1),12),('TOPPADDING',(0,0),(-1,-1),0),('BOTTOMPADDING',(0,0),(-1,-1),0), ('TOPPADDING',(0,0),(0,0),7),('BOTTOMPADDING',(0,n-1),(-1,n-1),6)])) t.spaceAfter = 10 return t ''' s=s[:a]+new+s[b:] # part merging: banner only keeps with first table (fine). Add RU to key s=s.replace(""" fl = [Paragraph(f'<font name="DV-B" color="{DARK[id(col)]}">{title}</font>', st_sm)] + [Paragraph(l, st_sm) for l in lines]""", """ fl = [Paragraph(f'<font name="DV-B" color="{DARK[id(col)]}">{title}</font>', st_sm)] for l in lines: if isinstance(l, tuple): fl += [Paragraph(l[0], st_sm), Paragraph(l[1], st_smru)] else: fl.append(Paragraph(l, st_sm))""") open('build3.py','w',encoding='utf-8').write(s) EOF grep -n "keyblock('M3" -A3 build3.py | head

Running Command

cd /tmp/workspace/participle-handbook/src && python - <<'EOF' s=open('build3.py',encoding='utf-8').read() def cut(s, start, end): a=s.index(start); b=s.index(end); return a,b # M3 a,b=cut(s,"story += keyblock('M3","story += keyblock('M4") s=s[:a]+'''story += keyblock('M3 Timeline Lab', [ ('1. Walking along the beach, I listened to music.', 'Идя по пляжу, я слушал музыку.'), ('2. Having finished her coffee, she left the café.', 'Допив кофе, она вышла из кафе.'), ('3. Waiting for his flight, he read a novel.', 'Ожидая свой рейс, он читал роман.'), ('4. Having checked the weather forecast, we packed our umbrellas.', 'Проверив прогноз погоды, мы взяли зонты.'), ('5. Playing in the garden, the kids got very dirty.', 'Играя в саду, дети сильно испачкались.'), ('6. Having read the instructions, I assembled the shelf.', 'Прочитав инструкцию, я собрал полку.')], GREEN, L_GREEN) '''+s[b:] a,b=cut(s,"story += keyblock('M5","story += keyblock('M6") s=s[:a]+'''story += keyblock('M5 Story Studio', [ '1. Feeling 2. Not having 3. Eating 4. Covered 5. Knowing 6. Having left 7. making 8. Seeing', ('Full story: Feeling lazy, Ellie stayed in bed until noon. Not having any food at home, she ordered a burger online. Eating too fast, she spilled ketchup on her new white top. Covered in red stains, the top looked terrible. Knowing that her friends were arriving at six, she rushed to the sink. Having left the top in cold water, she went to tidy her hair. The doorbell rang, making her jump. Seeing her wet sleeves, her friends burst out laughing.', 'Полная история: см. русский перевод в задании 5 (каждое предложение переведено под текстом).')], PURPLE, L_PURPLE) '''+s[b:] a,b=cut(s,"story += keyblock('M6","story += keyblock('M7") s=s[:a]+'''story += keyblock('M6 Sentence Surgeon', [ ('1. Feeling tired, I went to bed early.', 'Почувствовав усталость, я рано лёг спать.'), ('2. Shocked by the news, she dropped her cup.', 'Шокированная новостью, она уронила чашку.'), ('3. Having booked the tickets online, we skipped the queue.', 'Забронировав билеты онлайн, мы пропустили очередь.'), ('4. Not knowing the password, he called the IT desk.', 'Не зная пароля, он позвонил в службу поддержки.'), ('5. Having been left alone for an hour, the kids built a fort from cushions.', 'Оставшись одни на час, дети построили крепость из подушек.'), ('6. The storm destroyed the fence, flooding the garden.', 'Буря разрушила забор, затопив сад.'), ('7. Opening the oven, I burnt my hand.', 'Открывая духовку, я обжёг руку.'), ('8. Being the oldest sibling, Zoe looked after the others.', 'Будучи старшей из детей, Зои присматривала за остальными.')], PINK, L_PINK) '''+s[b:] a,b=cut(s,"story += keyblock('M7","story += keyblock('M8") s=s[:a]+'''story += keyblock('M7 Dangling Doctor (possible fixes)', [ ('1. The dog is jumping. Sitting on the sofa, I felt the dog jump onto my lap.', 'Прыгает собака. Сидя на диване, я почувствовал, как собака прыгнула мне на колени.'), ('2. The alarm cannot cook. Cooking dinner, I set off the smoke alarm.', 'Сигнализация не умеет готовить. Готовя ужин, я включил пожарную сигнализацию.'), ('3. The bed is not tired. Tired after the trip, I thought the bed looked wonderful.', 'Кровать не устала. Устав после поездки, я подумал, что кровать выглядит чудесно.'), ('4. The map does not lack knowledge. Not knowing the way, we found the map useless.', 'Карта не может не знать дороги. Не зная дороги, мы сочли карту бесполезной.'), ('5. The car is not waiting. Waiting at the bus stop, I was splashed by a car.', 'Машина не ждёт. Ожидая на остановке, я был обрызган машиной.'), ('6. The lights did not watch. Having watched the film, we saw the lights come on. (or: When the film ended, the lights came on.)', 'Свет не смотрел фильм. Посмотрев фильм, мы увидели, как включился свет. (или: Когда фильм закончился, включился свет.)')], PURPLE, L_PURPLE) '''+s[b:] a,b=cut(s,"story += keyblock('M9","doc.build") if False else (s.index("story += keyblock('M9"), s.index("new=[]; i=0")) s=s[:a]+'''story += keyblock('M9 Boss Level', [ ('1. Having returned (or: Returning) home, I switched on the kettle at once.', 'Вернувшись домой, я сразу включил чайник.'), ('2. Not knowing what to say, she smiled.', 'Не зная, что сказать, она улыбнулась.'), ('3. Woken by the alarm clock, he got up reluctantly.', 'Разбуженный будильником, он нехотя встал.'), ('4. Having had dinner, we went for a walk.', 'Поужинав, мы пошли гулять.'), ('5. Being late (or: Running late) for the meeting, Lena took a taxi.', 'Опаздывая на встречу, Лена взяла такси.'), ('6. The shop closed early, leaving us without bread.', 'Магазин закрылся рано, и мы остались без хлеба.')], PURPLE, L_PURPLE) '''+s[b:] s=s.replace("OUT = '/tmp/workspace/participle-handbook/output/Participle_Clauses_Handbook_v2.pdf'","OUT = '/tmp/workspace/participle-handbook/output/Participle_Clauses_Handbook_v3.pdf'") open('build3.py','w',encoding='utf-8').write(s) EOF python build3.py 2>&1 | tail -5 && python -c " import pypdfium2 as p from PIL import Image d=p.PdfDocument('/tmp/workspace/participle-handbook/output/Participle_Clauses_Handbook_v3.pdf') n=len(d); print(n) ims=[d[i].render(scale=0.62).to_pil() for i in range(n)] w,h=ims[0].size for k in range(0,n,8): m=Image.new('RGB',(w*4,h*2),'white') for j,im in enumerate(ims[k:k+8]): m.paste(im,((j%4)*w,(j//4)*h)) m.save(f'/tmp/workspace/participle-handbook/src/n{k//8}.png') "

Reading File
Reading File
Reading File
Running Command

cd /tmp/workspace/participle-handbook/src && python - <<'EOF' s=open('build3.py',encoding='utf-8').read() s=s.replace("rows = [[top + exrow(1, exs[0]) + exrow(2, exs[1])]] + [[exrow(i, ex)] for i, ex in enumerate(exs[2:], 3)]", "rows = [[top + exrow(1, exs[0]) + exrow(2, exs[1])], [exrow(3, exs[2])], [exrow(4, exs[3]) + exrow(5, exs[4])]]") open('build3.py','w',encoding='utf-8').write(s) EOF python build3.py | tail -1 && python -c " import pypdfium2 as p from PIL import Image d=p.PdfDocument('/tmp/workspace/participle-handbook/output/Participle_Clauses_Handbook_v3.pdf') n=len(d); print(n) ims=[d[i].render(scale=0.55).to_pil() for i in range(1,13)] w,h=ims[0].size m=Image.new('RGB',(w*6,h*2),'white') for j,im in enumerate(ims): m.paste(im,((j%6)*w,(j//6)*h)) m.save('/tmp/workspace/participle-handbook/src/z.png'); print(m.size) "

Reading File
Running Command

cd /tmp/workspace/participle-handbook/src && python -c " from PIL import Image im=Image.open('z.png').convert('RGB'); im=im.resize((im.size[0]*3//4, im.size[1]*3//4)); im.save('z.jpg', quality=70) import os; print(os.path.getsize('z.jpg'))"; rm -f /tmp/workspace/participle-handbook/src/*.png; ls /tmp/workspace/participle-handbook/output

Reading File
I rebuilt the handbook and it is now a 24-page PDF. I checked the layout by rendering the pages.

Participle Clauses Handbook v3

PDF Document · PDF

You didn't say what was wrong with the last version, so I fixed what I could see:
  • Page layout: the last version left large blank gaps on several pages. In this one the rule boxes flow from page to page. A box can now continue on the next page, which saves paper but means a few boxes are split across two pages.
  • Answer key: the key now has a Russian translation under each corrected sentence in Missions 3, 6, 7 and 9. For Mission 5 it gives the full story in English with a short pointer to the Russian lines in the task.
  • Worksheet: the scoreboard now fits ("Total" was cut off before). The mission headers also stay with their tasks instead of being left alone at the bottom of a page.
Everything you asked for last time is still in:
  • Every rule has 5 daily-life examples in English with the Russian translation under each one.
  • Nothing about relative clauses.
  • A 9-mission worksheet and an answer key at the end. The worksheet runs from spotting participle clauses to choosing forms, then writing, creating and translating.
If something else was wrong, tell me what. For example, you might want more Russian explanation, shorter rule sections, or a different worksheet style. I can then change that part without redoing everything.
This is a shared conversation. Sign in to Orris to start your own chat.