#!/usr/bin/env python3 """Generate English SDS — proper @page margin-box header/footer, with working page numbers. WHAT CHANGED vs the original script, and why: 1. Header/footer are no longer `position: fixed`
s living in normal document flow. They are now `position: running(...)` elements, pulled into real CSS Paged Media margin boxes (`@top-center`, `@bottom-center`) via `@page` rules. This is the only mechanism WeasyPrint reliably supports for content that must repeat identically on every page and stay anchored to the page box rather than the flow. 2. `html, body { width: 100%; height: 100%; }` is set explicitly. Without an explicit containing-block size, a `position: fixed` box has nothing reliable to size itself against, which is one reason the old header/footer collapsed to the top of page 1 at a shrunk scale instead of spanning full page width. 3. Page numbers now live in the SAME @bottom-center margin box as the footer contact line, via one `content:` value list, e.g.: content: element(footer) "Page " counter(page) " of " counter(pages); Previously the footer contact info and the "Page X of Y" counter were assigned to two different margin boxes (@bottom-left vs @bottom-center) which don't stack or share a baseline — that's why the counter looked "missing" or floated separately. counter(page)/counter(pages) ONLY resolve inside an @page margin box's `content`, never inside ordinary flowed or fixed HTML — so this placement is mandatory, not a style choice. 4. Page geometry (@page margin values) is defined ONCE and reused consistently, sized to actually fit the running header/footer content (rule + 2 lines), instead of two different margin numbers copied between two files with different assumptions. All Section 1-16 content, tables, and copy are unchanged from the original script — only the header/footer/page-number plumbing was touched. """ import os, base64, re from weasyprint import HTML PRIMARY = "#004C98" SECONDARY = "#2B2E33" MUTED = "#5B6670" ACCENT = "#0071B8" HIGHLIGHT = "#F2F6FA" BORDER = "#D9E1E8" WHITE = "#FFFFFF" WARNING_AMBER = "#C77700" def img_b64(path): with open(path, 'rb') as f: return f"data:image/png;base64,{base64.b64encode(f.read()).decode()}" def svg_b64(path): with open(path, 'rb') as f: return f"data:image/svg+xml;base64,{base64.b64encode(f.read()).decode()}" logo_b64 = img_b64('/home/bachko/msds-tds/assets/logo-synthesia.png') iso_b64 = svg_b64('/home/bachko/msds-tds/assets/iso-tuv-rheinland.svg') html = f"""
SAFETY DATA SHEET
Ethyl acetate
SDS-ETHYL-ACETATE-REV1 · 06/07/2026
TÜV Rheinland

SAFETY DATA SHEET
Ethyl acetate

According to Regulation (EU) 2020/878 (REACH Annex II) · CLP Regulation (EC) 1272/2008 as amended by EU 2024/2865

SECTION 1: Identification of the substance/mixture and of the company/undertaking

1.1 Product identifier

PropertyValue
Product formSubstance
Chemical nameEthyl acetate
EC Index-No.607-022-00-5
EC-No.205-500-4
CAS-No.141-78-6
REACH registration No.01-2119475103-46-XXXX
Molecular formulaC₄H₈O₂

1.2 Relevant identified uses

Use of the substance/mixtureIndustrial use
Restrictions on useNo data available

1.3 Details of the supplier

FieldDetails
CompanySynthesia Ltd.
AddressTodor Radunchev 9, Krivina 1588, Bulgaria
VATBG121306542
Phone+359 2 974 4968
Emailoffice@synthesia.bg

1.4 Emergency telephone number

Emergency number112

SECTION 2: Hazards identification

2.1 Classification

Classification according to Regulation (EC) No. 1272/2008 [CLP] as amended by EU 2024/2865:

Hazard classCategoryHazard statement
Flammable liquidsCategory 2H225 — Highly flammable liquid and vapour
Serious eye damage/eye irritationCategory 2H319 — Causes serious eye irritation
Specific target organ toxicity — Single exposureCategory 3 (Narcosis)H336 — May cause drowsiness or dizziness

Full text of H-statements: see Section 16.

2.2 Label elements

ElementDetails
Hazard pictogramsGHS02 (Flame) · GHS07 (Exclamation mark)
Signal wordDanger
Hazard statementsH225 — Highly flammable liquid and vapour.
H319 — Causes serious eye irritation.
H336 — May cause drowsiness or dizziness.
Precautionary statementsP210 — Keep away from heat, hot surfaces, sparks, open flames and other ignition sources. No smoking.
P280 — Wear protective gloves/protective clothing/eye protection/face protection.
P304+P340 — IF INHALED: Remove person to fresh air and keep comfortable for breathing.
P305+P351+P338 — IF IN EYES: Rinse cautiously with water for several minutes. Remove contact lenses, if present and easy to do. Continue rinsing.
P337+P313 — If eye irritation persists: Get medical advice/attention.
P403+P233 — Store in a well-ventilated place. Keep container tightly closed.
EUH-statementsEUH066 — Repeated exposure may cause skin dryness or cracking.

2.3 Other hazards

This substance does not contain components considered to be persistent, bioaccumulative and toxic (PBT), very persistent and very bioaccumulative (vPvB), persistent, mobile and toxic (PMT), or very persistent and very mobile (vPvM) at levels of 0.1% or higher, in accordance with the criteria of CLP Regulation as amended by EU 2024/2865 and REACH Annex XIII.

This substance is not classified as an endocrine disruptor (ED) according to the criteria of CLP Regulation as amended by EU 2024/2865, Commission Delegated Regulation (EU) 2017/2100, or Commission Regulation (EU) 2018/605.

Vapours may form explosive mixture with air.

SECTION 3: Composition/information on ingredients

3.1 Substances

ComponentCAS-No.EC-No.Index-No.ClassificationConcentration
Ethyl acetate141-78-6205-500-4607-022-00-5Flam. Liq. 2; Eye Irrit. 2; STOT SE 3; H225, H319, H336> 99.8%

Molecular weight: 88.11 g/mol  |  Formula: C₄H₈O₂

3.2 Mixtures

Not applicable.

SECTION 4: First aid measures

4.1 Description of first aid measures

RouteMeasures
GeneralCall a poison centre or a doctor if you feel unwell.
After inhalationRemove person to fresh air and keep comfortable for breathing.
After skin contactRinse skin with water/shower. Take off immediately all contaminated clothing.
After eye contactRinse cautiously with water for several minutes. Remove contact lenses, if present and easy to do. Continue rinsing.
After ingestionRinse mouth. Do not induce vomiting. Call a poison centre or a doctor if you feel unwell.

4.2 Most important symptoms and effects

Symptoms/effectsDescription
GeneralMay cause drowsiness or dizziness.
Skin contactRepeated exposure may cause skin dryness or cracking.
Eye contactEye irritation.

4.3 Indication of immediate medical attention

Treat symptomatically.

SECTION 5: Firefighting measures

5.1 Extinguishing media

Suitable extinguishing mediaWater spray. Dry powder. Foam. Carbon dioxide.
Unsuitable extinguishing mediaHigh volume water jet.

5.2 Special hazards

Fire hazardHighly flammable liquid and vapour.
Hazardous decomposition productsToxic fumes may be released. Carbon oxides.

5.3 Advice for firefighters

Do not attempt to take action without suitable protective equipment.

SECTION 6: Accidental release measures

6.1 Personal precautions

PersonnelMeasures
For non-emergency personnelWear recommended PPE. Ventilate area. No open flames, no sparks, no smoking.
For emergency respondersEliminate all ignition sources. Evacuate unnecessary personnel.

6.2 Environmental precautions

Avoid release to the environment.

6.3 Methods for cleaning up

Methods for cleaning upTake up liquid spill into absorbent material.
Other informationDispose of residues at an authorised site.

6.4 Reference to other sections

Refer to protective measures listed in Sections 7 and 8.

SECTION 7: Handling and storage

7.1 Precautions for safe handling

Keep away from heat, hot surfaces, sparks, open flames and other ignition sources. No smoking. Ground/bond container and receiving equipment. Use only non-sparking tools. Take precautionary measures against static discharge. Use only outdoors or in a well-ventilated area.

Hygiene measures: Do not eat, drink or smoke when using this product.

7.2 Conditions for safe storage

Storage conditionsStore in a well-ventilated place. Keep cool. Keep container tightly closed.
IncompatibilitiesHeat and ignition sources.

7.3 Specific end use(s)

No additional information available.

SECTION 8: Exposure controls/personal protection

8.1 Control parameters

EU Occupational Exposure Limits (IOELV) — Ethyl acetate
IOELV TWA734 mg/m³ (200 ppm)
IOELV STEL1,468 mg/m³ (400 ppm)
ReferenceDirective (EU) 2017/164
DNEL/DMEL — Workers
Acute - inhalation1,468 mg/m³
Long-term - dermal63 mg/kg bw/day
Long-term - inhalation734 mg/m³
DNEL/DMEL — General population
Acute - inhalation734 mg/m³
Long-term - oral4.5 mg/kg bw/day
Long-term - inhalation367 mg/m³
Long-term - dermal37 mg/kg bw/day
PNEC
Freshwater0.24 mg/l
Marine water0.024 mg/l
Soil0.148 mg/kg dwt
STP650 mg/l

8.2 Exposure controls

Control typeMeasures
Engineering controlsEnsure good ventilation.
Hand protectionProtective gloves.
Eye protectionSafety glasses.
Respiratory protectionIf ventilation insufficient, wear suitable respiratory equipment.
Environmental exposureAvoid release to the environment.

SECTION 9: Physical and chemical properties

9.1 Information on basic physical and chemical properties

PropertyValue
Physical stateClear liquid
Colour / OdourColourless / Fruity
Melting point-84 °C
Boiling point76.5 – 77.5 °C
Flash point-4 °C
Auto-ignition426 °C
Flammability limits2 – 11.5 vol %
Vapour pressure73 mm Hg (20 °C)
Vapour density3.04 (Air = 1)
Density0.902 g/cm³ (20 °C)
Solubility (water)8 g/100 ml (25 °C)
Log Pow0.73
Viscosity0.45 mPa·s (20 °C)

SECTION 10: Stability and reactivity

10.1 Reactivity

Highly flammable liquid and vapour.

10.2 Chemical stability

Stable under normal conditions.

10.3 Hazardous reactions

No dangerous reactions known under normal conditions of use.

10.4 Conditions to avoid

Avoid contact with hot surfaces. No flames, no sparks. Eliminate all sources of ignition.

10.5 Incompatible materials

No additional information available.

10.6 Hazardous decomposition products

Under normal conditions of storage and use, hazardous decomposition products should not be produced.

SECTION 11: Toxicological information

11.1 Information on hazard classes

EndpointClassificationData
Acute toxicityNot classifiedLD50 oral: 5,620 mg/kg bw (rabbit)
Skin corrosion/irritationNot classified
Serious eye damage/irritationCauses serious eye irritation
Respiratory/skin sensitisationNot classified
Germ cell mutagenicityNot classified
CarcinogenicityNot classified
Reproductive toxicityNot classified
STOT — single exposureCategory 3 (Narcosis)May cause drowsiness/dizziness
STOT — repeated exposureNot classifiedNOAEL 900 mg/kg bw (90d rat)
Aspiration hazardNot classified

11.2 Other hazards

Not classified as having endocrine disrupting properties according to REACH Article 57(f), CLP as amended by EU 2024/2865.

SECTION 12: Ecological information

12.1 Toxicity

The product is not considered harmful to aquatic organisms.

EndpointValueSpecies
LC50 (fish, 96h)230 mg/lPimephales promelas
EC50 (daphnia, 48h)720 mg/lDaphnia magna
EC50 (algae, 72h)3,300 mg/l

12.2 Persistence and degradability

Readily biodegradable — 100% after 28 days.

12.3 Bioaccumulative potential

Log Pow0.73 (low)

12.4 Mobility in soil

Low mobility.

12.5 PBT and vPvB assessment

Not classified as PBT or vPvB.

12.6 Endocrine disrupting properties

Not classified as having ED properties.

12.7 Other adverse effects

No additional information available.

SECTION 13: Disposal considerations

13.1 Waste treatment methods

Waste treatment methodsDispose of contents/container in accordance with licensed collector's sorting instructions.
Packaging disposalDispose of contents/container to hazardous or special waste collection point.
Additional informationDo not re-use empty containers. Flammable vapours may accumulate.

SECTION 14: Transport information

In accordance with ADR / RID / IMDG / IATA / ADN

Transport modeUN No.Proper shipping nameClassPGEnv.
ADR (road)1173ETHYL ACETATE, 3, II, (D/E)3IINo
IMDG (sea)1173ETHYL ACETATE, 3, II3IINo
IATA (air)1173Ethyl acetate, 3, II3IINo
ADR Details
Classification codeF1
Limited quantities1 L
Excepted quantitiesE2
Tunnel restriction codeD/E
Kemler No.33
Transport category2

14.7 Transport in bulk

Not applicable.

SECTION 15: Regulatory information

15.1 Safety, health and environmental regulations

15.1.1 EU Regulations

15.1.2 National regulations

CountryRegulationStatus
GermanyWater hazard class (WGK)WGK 1 — Slightly hazardous to water (AwSV; ID No. 95)

15.2 Chemical safety assessment

No chemical safety assessment has been carried out.

SECTION 16: Other information

Abbreviations and acronyms

AbbreviationFull form
ADREuropean Agreement concerning the International Carriage of Dangerous Goods by Road
CASChemical Abstract Service number
CLPClassification, Labelling and Packaging Regulation (EC) No 1272/2008
DNELDerived No-Effect Level
IATAInternational Air Transport Association
IMDGInternational Maritime Dangerous Goods
PBTPersistent, Bioaccumulative, Toxic
PNECPredicted No-Effect Concentration
REACHRegulation (EC) No 1907/2006
STOTSpecific Target Organ Toxicity
vPvBVery Persistent and Very Bioaccumulative
WGKWater Hazard Class (Germany)

Full text of H- and EUH-statements

CodeText
H225Highly flammable liquid and vapour.
H319Causes serious eye irritation.
H336May cause drowsiness or dizziness.
EUH066Repeated exposure may cause skin dryness or cracking.

Disclaimer of Liability: The information in this SDS was obtained from sources which we believe are reliable. However, the information is provided without any warranty, express or implied, regarding its correctness. The conditions or methods of handling, storage, use or disposal of the product are beyond our control. We do not assume responsibility and expressly disclaim liability for loss, damage or expense arising out of or in any way connected with the handling, storage, use or disposal of the product.

""" def add_thead_tbody(html_str): """ Wrap each ...
's leading header row(s) in and the remaining rows in . Why this is needed: WeasyPrint only repeats a header row on every page a table spans if that row is inside a real element. The tables above are flat ...
...
...
markup with no / at all, so when a table splits across a page break, the header simply doesn't come back on the continuation page. This is done as a single automated pass instead of hand-editing every table, so every table.data element gets the fix consistently. """ table_pattern = re.compile(r'()(.*?)(
)', re.DOTALL) def process_table(match): open_tag, body, close_tag = match.group(1), match.group(2), match.group(3) rows = re.findall(r'.*?', body, re.DOTALL) if not rows: return match.group(0) header_rows = [] data_rows = [] for row in rows: # Leading row(s) containing are treated as the header; # once a row without appears, everything after is body. if '' + ''.join(header_rows) + '' '' + ''.join(data_rows) + '' ) return open_tag + new_body + close_tag return table_pattern.sub(process_table, html_str) html = add_thead_tbody(html) # ─── Generate ─── OUT_DIR = '/home/bachko/msds-tds/output' os.makedirs(OUT_DIR, exist_ok=True) html_path = os.path.join(OUT_DIR, 'sds-ethyl-acetate-en-fixed.html') with open(html_path, 'w', encoding='utf-8') as f: f.write(html) print(f"HTML: {html_path}") pdf_path = os.path.join(OUT_DIR, 'SDS_Ethyl_Acetate_REV1_EN_fixed.pdf') HTML(string=html).write_pdf(pdf_path) print(f"PDF: {pdf_path}")