python pdfKit
を使用してhtmlファイルからpdfファイルを作成することで、サービスを設定しようとしています。
したがって、基本的には要素を文字列として送信し、サーバーがそのpdfバージョンを返すことを期待しますが、正確な表現のために、要素のcssファイルも送信する必要があります。
これどうやってするの?要素とそのすべての子の関連するスタイルプロパティとセレクターのみでJSON /オブジェクトを生成します。階層を尊重し、重複はありません。同様の質問がありますが、それらは時代遅れであり、子供の要素を考慮しない傾向があります。
この要素から新しいDOMを作成し、ルートのCSSを取得する方法があるのではないかと考えていました。
これは私が思いついたものです、基本的には、スタイルを抽出する要素とその子の要素を渡します。これにより、スタイルシートが文字列として返されます。スニペットを実行する前にコンソールを開くと、console.log
からの出力が表示されます。
セレクターがないものも含めてすべての要素の抽出をサポートしたかったので、出力のスタイル設定を容易にするために、各要素IDをそれらのために特別に生成された一意のuuidに置き換える必要がありました。このアプローチの問題は、スタイリングまたはユーザー操作にIDを使用している場合、extractCSS
を呼び出した後に、関連する要素のそのような機能を失うことです。
ただし、oldId
プロセスが生成を完了したら、pdfKit
を使用して変更を戻すことは非常に簡単です。関数から返されたswapBackIds
を渡してelements
を呼び出すだけです。スニペットで呼び出しのコメントを外すと、動作の違いを確認できます。スタイリングが要素id
をターゲットにしているため、#root
ピンクの背景が消えます。
全体として、次のことを行う必要があります。
extractCSS
を呼び出しますres.stylesheet
を使用してPDFを生成しますswapBackIds
をres.elements
で呼び出す// Generate an unique id for your element
// From https://stackoverflow.com/a/2117523/2054072
function uuidv4 () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// Flatten an array
// https://stackoverflow.com/a/15030117/2054072
function flatten(arr) {
return arr.reduce(function (flat, toFlatten) {
return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
}, []);
}
function recursiveExtract (element) {
var id = uuidv4()
var oldId = element.id
var computed = window.getComputedStyle(element)
var style = computed.cssText
// Now that we get the style, we can swap the id
element.setAttribute('id', id)
// The children are not a real array but a NodeList, we need to convert them
// so we can map over them easily
var children = Array.prototype.slice.call(element.children)
return [{ id: id, style: style, oldId: oldId }].concat(children.map(recursiveExtract))
}
function extractCSS (element) {
if (!element) { return { elements: [], stylesheet: '' } }
var raw = recursiveExtract(element)
var flat = flatten(raw)
return {
elements: flat,
stylesheet: flat.reduce(function (acc, cur) {
var style = '#' + cur.id + ' {\n' + cur.style + '\n}\n\n'
return acc + style
}, '')
}
}
var pdfElement = document.querySelector('#root')
var res = extractCSS(pdfElement)
console.log(res.stylesheet)
function swapBackIds (elements) {
elements.forEach(function (e) {
var element = document.getElementById(e.id)
element.setAttribute('id', e.oldId)
})
}
swapBackIds(res.elements)
#root {
background-color: pink;
}
.style-from-class {
background-color: red;
width: 200px;
height: 200px;
}
.style-from-id {
background-color: green;
width: 100px;
height: 100px;
}
<div id="root">
<span>normal</span>
<span style="background: blue">inline</span>
<div class="style-from-class">
style-class
</div>
<div class="style-from-id">
style-id
<div style="font-size: 10px">a very nested</div>
<div style="font-size: 12px; color: white">and another</div>
</div>
</div>
<div id="ignored-sibling">
</div>
let para = document.querySelector('p');
let compStyles = window.getComputedStyle(para);
para.textContent = 'My computed font-size is ' + compStyles.getPropertyValue('font-size') + ',\nand my computed background is ' + compStyles.getPropertyValue('background') + '.';
p {
width: 400px;
margin: 0 auto;
padding: 20px;
font: 2rem/2 sans-serif;
text-align: center;
background: purple;
color: white;
}
<p>Hello</p>
getComputedStyle
メソッドを使用して、スタイルプロパティの計算値を取得できます